使用Ninject和Filter属性进行ASP.NET MVC的依赖注入

56

我正在为 asp.net mvc 3 编写自定义授权过滤器。我需要将一个用户服务注入到该类中,但我不知道如何实现。

public class AuthorizeAttribute : FilterAttribute, IAuthorizationFilter
{
    private IUserService userService;
    private string[] roles;

    public AuthorizeAttribute(params string[] roles)
    {
        this.roles = roles;
    }

    public void OnAuthorization(AuthorizationContext filterContext)
    {
        throw new NotImplementedException();
    }
}

我正在使用Ninject进行依赖注入。我不想使用工厂模式或服务定位器模式。

我的绑定在global.acsx中看起来像这样:

    internal class SiteModule : NinjectModule
    {
        public override void Load()
        {
            Bind<IUserService>().To<UserService>();
        }
    }

你确定不想使用工厂模式吗?如果是这样,你需要在你的Application类中保留对kernel的引用,并在控制器构造函数中手动获取<IUserService>()。在Mvc 3中,工厂模式确实很有意义,因为你要覆盖原始的工厂。也有适用于此的ninject nugets。 - jakubmal
Jakubmal,你能用工厂模式来回答吗? - Shawn Mclean
4个回答

82

参考此答案:Custom Authorization MVC 3 and Ninject IoC

如果想使用构造函数注入,那么您需要创建一个属性和一个过滤器。

/// Marker attribute
public class MyAuthorizeAttribute : FilterAttribute { }

/// Filter
public class MyAuthorizeFilter : IAuthorizationFilter
{
      private readonly IUserService _userService;
      public MyAuthorizeFilter(IUserService userService)
      {
          _userService = userService;
      }

      public void OnAuthorization(AuthorizationContext filterContext)
      {
          var validUser = _userService.CheckIsValid();

          if (!validUser)
          {
              filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary { { "action", "AccessDenied" }, { "controller", "Error" } });
          }
      }
}

绑定(Binding):

this.BindFilter<MyAuthorizeFilter>(System.Web.Mvc.FilterScope.Controller, 0).WhenControllerHas<MyAuthorizeAttribute>();

控制器:

[MyAuthorizeAttribute]
public class YourController : Controller
{
    // ...
}

7
如何设置实现以适应[MyAuthorizeAttribute("Admin", "Contributer")]?传递参数。 - Shawn Mclean
4
支持使用构造函数注入而不是属性注入。以下链接包含了有关在Ninject中配置过滤器的详细信息:https://github.com/ninject/ninject.web.mvc/wiki/Filter-configurations - Remo Gloor
1
@one.beat.consumer https://dev59.com/n1fUa4cB1Zd3GeqPLc53#6205526 - B Z
最近的 Ninject 版本中是否移除了 BindFilter 方法?我似乎无法让它识别并建议命名空间。 - kmehta
@kmehta请确保您已经引用了Ninject.Web.Mvc。 - B Z
显示剩余6条评论

11

强烈推荐B Z的答案不要使用[Inject]

我曾像Darin Dimitrov所说的那样使用[Inject],但实际上,在高负载、高竞争情况下,配合.InRequestScope使用会导致线程问题。

B Z的方式也是维基百科上的方式,我在很多地方看到了Remo GloorNinject作者)说这是正确的方法,例如https://github.com/ninject/ninject.web.mvc/wiki/Filter-configurations

在这里对[Inject]的答案进行负面评价,因为你会被“烧伤”(如果你事先没有进行适当的负载测试,很可能会在生产环境中遇到问题!)。


我已经这样做了,但由于默认情况下它被隐藏得太深,没有人会看到它。基于我花了三天时间试图弄清楚Darin的答案出了什么问题,我认为它非常相关且值得成为正式答案,以避免任何人错误地使用它并受到伤害。 - John Culviner

9
我发现了一个简单的解决方案,适用于任何不使用Ninject处理构建的情况:
var session = (IMyUserService)DependencyResolver.Current.GetService(typeof (IMyUserService));

实际上这正是我在使用自定义的AuthorizeAttribute时所用的方法。比起实现一个单独的FilterAttribute来说,这要容易得多。


8

一种方法是使用属性注入,并在属性上添加[Inject]属性:

public class AuthorizeAttribute : FilterAttribute, IAuthorizationFilter
{
    [Inject]
    public IUserService UserService { get; set; }

    private string[] roles;
  
    ...
}

构造函数注入在属性方面表现不佳,因为您将无法再使用它们来装饰控制器/操作。您只能使用Ninject中的过滤器绑定语法来进行构造函数注入:

public class AuthorizeAttribute : FilterAttribute, IAuthorizationFilter
{
    private readonly IUserService userService;

    private string[] roles;

    public AuthorizeAttribute(IUserService userService, params string[] roles)
    {
        this.userService = userService;
        this.roles = roles;
    }
  
    ...
}

然后:

internal class SiteModule : Ninject.Modules.NinjectModule
{
    public override void Load()
    {
        Bind<IUserService>().To<UserService>();

        this.BindFilter<AuthorizeAttribute>(FilterScope.Controller, 0)
            .WhenControllerType<AdminController>();
    }
}

BindFilter<> 扩展方法定义在 Ninject.Web.Mvc.FilterBindingSyntax 命名空间中,因此在调用内核上的该方法之前,请确保已将其引入到作用域中。


如果我不使用构造函数绑定而使用[Inject]属性,我能够装饰控制器/操作吗? - Shawn Mclean
1
你有什么想法为什么UserService仍然为空?我之前在Providers也遇到了同样的问题。 - Shawn Mclean
1
@Lol 程序员,我不知道。它对我来说很好用。我创建了一个新的ASP.NET MVC 3应用程序,安装了NInject.MVC3 NuGet包,声明了一个IUserService接口和UserService实现,声明了使用属性注入的自定义MyAuthorizeAttribute,并使用生成的App_Start/NinjectMVC3.cs配置内核。然后我只需在我的HomeController上使用该属性进行修饰,注入就能正常工作。 - Darin Dimitrov
1
感谢指定命名空间!!! 必须为扩展方法查找所需的命名空间,非常让人烦恼! - John B
2
我强烈推荐B Z的答案。不要使用[Inject],在高负载下可能会产生竞态条件! - John Culviner
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接