可选注入当前用户的最佳实践

das*_*dot 4 .net c# asp.net-mvc dependency-injection ninject

在我们的ASP.NET MVC项目中,我们使用Ninject来解析控制器所需的依赖项.

其中一个依赖项是当前用户HttpContext.Current.User.Identity.如果用户经过身份验证,我们希望实例化一个用户对象和几个依赖它的服务.但我们不想手动执行此操作,而是让ninject将这些实例注入控制器.

所以我们现在遇到了麻烦,因为当然可以找到一个url而不进行身份验证.然后ninject尝试在asp.net重定向到登录页面之前解析实例.

我可以想到解决方案,我们配置ninject只是在用户通过身份验证时注入:

kernel.Bind<User>().ToMethod(GetUser).When(context => HttpContext.Current.User.Identity.IsAuthenticated).InRequestScope();
Run Code Online (Sandbox Code Playgroud)

这里的问题是,即使用户未经过身份验证,ninject也会实例化一个默认对象,因此无论如何我的服务都会崩溃或需要检查实例.空检查会让我更容易接受,但我不想激活AllowNullInjectionNinject的设置.

所以我的问题是做有条件的事情的最佳做法是什么?我可以在这些情况下使用Ninject功能,还是不应该注入这些依赖项?

Ada*_*ger 5

我假设您正在讨论一种情况,即未经过身份验证的用户可能会尝试导航到通常需要身份验证的页面,但无需先完成登录过程.然后Ninject将无法将当前用户对象注入控制器,因为它尚未知道并将引发异常.

我可以看到2个选项:

第一个选项不是注入当前用户,而是创建一个工厂或提供程序来检索当前用户详细信息并将其注入.然后,控制器可以调用提供程序以获取当前用户,如果用户不可用,您可以重定向到登录页面.

public OrdersController(IUserProvider userProvider)
{
    this.userProvider = userProvider
}

public void DoSomething()
{
    var user = this.userProvider.GetCurrentUser();
    if (user == null)
        RedirectToLogin();

    // continue doing something
}

public class UserProvider : IUserProvider
{
    public User GetCurrentUser() { ... }
}
Run Code Online (Sandbox Code Playgroud)

此选项的问题在于,您需要在许多控制器中执行此操作(这是"交叉问题"),并且您不希望重复执行重定向的代码.相反,第二种选择是使用Decorator设计模式来创建拦截器,该拦截器在转发到真实控制器之前检查登录的用户.

我过去做类似事情的方式是使用Ninject拦截扩展来创建一个标记哪些控制器需要身份验证的属性,比如这个(位伪代码):

public class AuthenticationInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        bool authenticated = // ... get the current user ...

        if (authenticated)
            invocation.Proceed();
        else
            RedirectToLoginPage(); // however you want to do this
    }
}

public class RequiresAuthenticationAttribute : InterceptAttribute
{
    public override IInterceptor CreateInterceptor(IProxyRequest request)
    {
        return request.Context.Kernel.Get<AuthenticationInterceptor>();
    }
}

[RequiresAuthentication]
public class OrdersController : IOrdersController
{
    // assume you've already been authenticated
}
Run Code Online (Sandbox Code Playgroud)

只要创建了用于修饰的类RequiresAuthentication并且将检查当前用户凭据,就会自动创建拦截器.如果它们无效,请求将被转发到登录页面,否则它将继续正常.这个拦截器然后可以被编写和测试一次,同时在许多地方使用而不需要复制代码.