【问题标题】:Best practise for optional injecting of current user可选注入当前用户的最佳实践
【发布时间】:2013-07-02 13:41:31
【问题描述】:

在我们的 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();

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

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

【问题讨论】:

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


    【解决方案1】:

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

    我可以看到 2 个选项:

    第一个选项不是注入当前用户,而是创建一个工厂或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() { ... }
    }
    

    此选项的问题在于,您可能需要在许多控制器中执行此操作(这是一个“横切关注点”),并且您不想一遍又一遍地重复执行重定向的代码。相反,第二种选择是使用装饰器设计模式创建一个拦截器,在转发到真实控制器之前检查登录用户。

    我过去做过类似事情的方式是使用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 似乎很简单,对于我的情况应该足够了。无论如何,我希望这个案子可以由 ninject 自己处理..
    【解决方案2】:

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

            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 更好:)

    【讨论】:

      猜你喜欢
      • 2019-05-23
      • 2013-04-21
      • 2010-12-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-28
      相关资源
      最近更新 更多