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

时间:2013-07-02 13:41:31

标签: c# .net asp.net-mvc dependency-injection ninject

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

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

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

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

kernel.Bind<User>().ToMethod(GetUser).When(context => HttpContext.Current.User.Identity.IsAuthenticated).InRequestScope();

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

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

2 个答案:

答案 0 :(得分:5)

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

我可以看到两个选项:

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

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() { ... }
}

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

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

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
}

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

答案 1 :(得分:0)

就像一个简单的身份验证和非身份验证的答案,有些人可能觉得有用。

        kernel.Bind<ICustomUser>()
            .To<User>()
            .When(ctx => HttpContext.Current.User.Identity.IsAuthenticated)
            .InRequestScope();

        kernel.Bind<ICustomUser>()
            .To<Guest>()
            .When(ctx => !HttpContext.Current.User.Identity.IsAuthenticated)
            .InRequestScope();

否则任何更复杂的Adam Rodgers awnser都会更好:)