【问题标题】:Injecting dependencies into custom Web API action filter attribute with Autofac使用 Autofac 将依赖项注入自定义 Web API 操作过滤器属性
【发布时间】:2014-04-25 19:15:20
【问题描述】:

我正在尝试解决我的自定义 AuthorizeAttribute 的依赖关系,我用它来装饰 MVC4 应用程序中的 API 控制器。问题是我在自定义过滤器中使用的服务依赖项上不断收到NullReferenceException。这是我的 Autofac 配置:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        var builder = new ContainerBuilder();
        builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
        builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerApiRequest();
        builder.RegisterType<DatabaseFactory>().As<IDatabaseFactory>().InstancePerApiRequest();
        builder.RegisterAssemblyTypes(typeof(UserProfileRepository).Assembly)
            .Where(t => t.Name.EndsWith("Repository"))
            .AsImplementedInterfaces().InstancePerApiRequest();

        builder.RegisterAssemblyTypes(typeof(IUserProfileMapper).Assembly)
            .Where(t => t.Name.EndsWith("Mapper"))
            .AsImplementedInterfaces().InstancePerApiRequest();

        builder.RegisterAssemblyTypes(typeof(UserProfileSvc).Assembly)
            .Where(t => t.Name.EndsWith("Svc"))
            .AsImplementedInterfaces().InstancePerApiRequest();

        builder.RegisterWebApiFilterProvider(config);
        var container = builder.Build();
        var resolver = new AutofacWebApiDependencyResolver(container);
        config.DependencyResolver = resolver;
    }
}

和我的自定义授权过滤器:

public class MyAuthorizeAttribute : AuthorizeAttribute
{
    public IAuthenticationSvc _authenticationSvc;
    protected override bool IsAuthorized(System.Web.Http.Controllers.HttpActionContext actionContext)
    {
        if (!base.IsAuthorized(actionContext))
        {
            return false;
        }
        var trueUserId = WebSecurity.CurrentUserId;

        if (_authenticationSvc.GetUsersRoles(trueUserId).Any(x => x == "Admin")) return true;
        // NullReferenceException on _authenticationSvc
     }
}

根据official docs,所需要的只是:

var builder = new ContainerBuilder();
builder.RegisterWebApiFilterProvider(GlobalConfiguration.Configuration);

但这似乎也不起作用。感谢任何帮助。

【问题讨论】:

  • 我认为 _authenticationSvc 需要是一个属性而不是一个字段
  • @MikeSW 现在它给了我这个例外:No scope with a Tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested. This generally indicates that a component registered as per-HTTP request is being requested by a SingleInstance() component (or a similar scenario.) Under the web integration always request dependencies from the DependencyResolver.Current or ILifetimeScopeProvider.RequestLifetime, never from the container itself.

标签: asp.net-mvc asp.net-web-api dependency-injection autofac action-filter


【解决方案1】:

我认为 Autofac 的文档为 WebApi 操作过滤器提供了更简单的解决方案。

public interface ServiceCallActionFilterAttribute : ActionFilterAttribute
{
  public override void OnActionExecuting(HttpActionContext actionContext)
  {
    // Get the request lifetime scope so you can resolve services.
    var requestScope = actionContext.Request.GetDependencyScope();

    // Resolve the service you want to use.
    var service = requestScope.GetService(typeof(IMyService)) as IMyService;

    // Do the rest of the work in the filter.
    service.DoWork();
  }
}

它不是“纯 DI”,因为它使用服务定位器,但它很简单并且适用于请求范围。您无需担心为每个 WebApi 控制器注册特定的操作过滤器。

来源: http://autofac.readthedocs.io/en/latest/integration/webapi.html#provide-filters-via-dependency-injection

【讨论】:

  • 我不得不说我更喜欢这个而不是属性注入。
  • 这很好理解,但如果你打算编写测试,属性注入是一个更简单的解决方案。
  • @PhilCooper 在单元测试期间,您最终会遇到此类过滤器的问题。那我们应该怎么模拟呢?
  • @AlexHerman 您可以提供一个您的测试将使用的构造函数,该构造函数将为解析类型提供Func&lt;HttpActionContext, IService&gt; serviceFactory 机制,而您的默认(空)构造函数将简单地使用上面的示例。 actionContext =&gt; actionContext.Request.GetService(typeof(IMyService)) as IMyService.
  • 现在工作了,我忘了在 AutoFac 中注册我的服务
【解决方案2】:

你应该为你的属性配置属性注入

public class MyAuthorizeAttribute : AuthorizeAttribute
{
    public IAuthenticationSvc AuthenticationSvc { get; set; }
}

和建设者

builder.RegisterType<MyAuthorizeAttribute>().PropertiesAutowired();

【讨论】:

  • 我认为这个答案可能有效,但它不是正确的。因为 webApi 中的“AuthorizeAttribute”和完全过滤器只有一个实例,如果您同时有请求,可能会出现一些问题。事实上,这不是一个线程安全的解决方案。我说的对吗?
  • @MohammadRezaDaghestani 我相信你是对的,因为这就是我在自动取款机上面临的问题。
【解决方案3】:

除了@Toan Nguyen 的回答,如果你有这个...

public class MyAuthorizeAttribute : AuthorizeAttribute
{
    public IAuthenticationSvc AuthenticationSvc { get; set; }
}

...看来您还需要(或可能需要)下面的第一行:

builder.RegisterFilterProvider();
builder.RegisterType<MyAuthorizeAttribute>().PropertiesAutowired();

参考:http://itprojectpool.blogspot.com.au/2014/03/autofac-di-on-action-filters.html

【讨论】:

  • 是的,第一行已经帮我解决了。
  • 嗯,这是针对 MVC 的吧?问题中的示例代码用于 WebAPI 注册。虽然这个问题也被标记为 MVC,所以这个信息是相关的,但我认为 RegisterFilterProvider 方法是等同于 RegisterWebApiFilterProvider 的 MVC 应该很清楚
【解决方案4】:

除了配置属性注入,如其他答案中所述,您还可以在 OnActivating 回调中显式解析依赖项。

public class MyAuthorizeAttribute : AuthorizeAttribute
{
    private IAuthenticationSvc _authenticationSvc;
    public void SetAuthenticationSvc(IAuthenticationSvc svc)
    {
       this._authenticationSvc = svc;
    }
}

然后注册类型:

builder.RegisterType<MyAuthorizeAttribute>()
    .OnActivating(_ => _.Instance.SetAuthenticationSvc(_.Context.Resolve<IAuthenticationSvc>()));

注意:您可以对属性而不是方法执行相同的操作。我这里选择使用一种方法只是为了说明这个解决方案不依赖于PropertiesAutowired

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-13
    • 1970-01-01
    • 1970-01-01
    • 2013-02-02
    相关资源
    最近更新 更多