如何创建可在应用程序范围内访问的上下文对象,以在数据访问中存储当前用户详细信息?



我有一个n层Web应用程序,其中包含Web UserInterface,BusinessLogicLayer和DataAccessLayer。我正在寻找最好的解决方案,将我当前登录用户的详细信息一直传递到数据访问,而无需将其添加到我的所有方法签名中。审核需要用户详细信息。我相信您可以创建一个应用程序范围内可用的上下文,有没有人这样做的例子?我正在寻找最好的设计模式,将我的担忧分开。

以下是两种方法:

首先
如果其他层确实需要了解用户,则此方法更有意义。例如,他们可能会检查权限或根据用户身份做出一些决定。

您可以创建一个抽象或接口来描述您希望这些类能够访问的内容,如下所示:

public interface IUserContext
{
SomeClassContainingUserData GetCurrentUser();
}

我会根据使用者类的需求来定义该用户数据类,而不仅仅是使用一些现有的Customer类,以防止它与您的 Web 应用程序紧密耦合。

现在,您可以将该类注入到其他类中:

public class MyBusinessLogicClass
{
private readonly IUserContext _userContext;
public MyBusinessLogicClass(IUserContext userContext)
{
_userContext = userContext;
}
public void SomeOtherMethod(Whatever whatever)
{
var user = _userContext.GetCurrentUser();
// do whatever you need to with that user
}
}

这使您的其他类可测试,因为很容易注入返回所需内容的接口模拟,以便您可以确保您的类针对不同类型的用户正确运行。

如果您的用户数据来自HttpContext那么您的运行时实现可能大致如下所示:

public class HttpUserContext
{
private readonly IHttpContextAccessor _contextAccessor;
public HttpUserContext(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
public SomeClassContainingUserData GetCurrentUser()
{
var httpContext = _contextAccessor.HttpContext;
// get the user data from the context and return it.
}
}

这是一个粗略的轮廓。一个考虑因素是范围界定。如果所有对象都是按请求限定的范围,则IUserContext实现可以生成一次用户数据并将其存储在成员变量中,而不是一遍又一遍地访问它。

缺点是必须在任何地方注入它,但如果这些类需要这些信息,这是不可避免的。

其次
,如果这些内部类实际上根本不需要用户信息怎么办?如果您只想记录哪些用户发出了由这些类处理的请求,该怎么办?如果要使用单独的对象来检查权限,该怎么办?

在这种情况下,选项将是拦截器或包装器。在最简单的形式中,它可能看起来像这样:

public class SomeBusinessClassSecurityInterceptor : ISomeBusinessClass
{
private readonly ISomeBusinessClass _inner;
private readonly IUserContext _userContext;
public SomeBusinessClassSecurityInterceptor(
ISomeBusinessClass inner, IUserContext userContext)
{
_inner = inner;
_userContext = userContext;
}
public void UpdateSomeData(Foo data)
{
if(!CanUpdate()) 
throw new YouCantUpdateThisException();
_inner.UpdateSomeData(data);
}
private bool CanUpdate()
{
var user = _userContext.GetCurrentUser();
// make some decision based on the user
}   
}

如果检索用户的权限更复杂,您可能希望有一个IUserPermissions并注入该而不是IUserContext。然后将IUserContext注入IUserPermissions的实现中。在运行时,它检索当前用户,然后执行自己的操作来确定用户具有哪些权限。

如果你有很多类和方法,那么维护单独的包装类可能会变得乏味。另一种选择是使用拦截器,这可能意味着使用不同的依赖注入容器,如 Windsor 或 Autofac。这些特别适合日志记录。

以Autofac为例,这意味着编写一个这样的类:

public class LoggingInterceptor : IInterceptor
{
private readonly IUserContext _userContext;
private readonly ILogger _logger;
public CallLogger(IUserContext userContext, ILogger logger)
{
_userContext = userContext;
_logger = logger;
}
public void Intercept(IInvocation invocation)
{
var user = _userContext.GetCurrentUser();
_logger.Log( 
// some stuff about the invocation, like method name and maybe parameters)
// and who the user was.
// Or if you were checking permissions you could throw an exception here.
invocation.Proceed();
}
}

然后,您将告诉容器,对给定类的"真实"实现的所有调用都通过此拦截器(在其文档中描述得很好)。

如果您正在检查权限,则可以注入IUserPermissions.拦截器可以检查内部方法上的属性,该属性指定所需的权限,并将其与当前用户的权限进行比较。

您通常可以编写一个拦截器并将其与许多其他类一起使用,因为它不需要知道有关内部目标类的任何信息。但是,如果需要,您还可以编写仅用于某些类的更窄用途的拦截器。

好的是,它为您提供了很大的灵活性,但不涉及业务逻辑或数据类的实际接口或实现。他们可以专注于自己的单一职责,而其他类则配置为记录向他们发出的请求或检查用户权限。

最新更新