如何在 ASP.NET CORE 中使用具有依赖注入的动作过滤器?

Posted

技术标签:

【中文标题】如何在 ASP.NET CORE 中使用具有依赖注入的动作过滤器?【英文标题】:How to use Action Filters with Dependency Injection in ASP.NET CORE? 【发布时间】:2017-01-08 10:35:47 【问题描述】:

我在我的ASP.NET CORE 应用程序中到处使用基于构造函数的依赖注入,我还需要在我的操作过滤器中解决依赖关系:

public class MyAttribute : ActionFilterAttribute

    public int Limit  get; set;  // some custom parameters passed from Action
    private ICustomService CustomService  get;  // this must be resolved

    public MyAttribute()
    
    

    public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    
        // my code
        ...

        await next();
    

然后在控制器中:

[MyAttribute(Limit = 10)]
public IActionResult()

    ...

如果我将 ICustomService 放入构造函数,那么我将无法编译我的项目。那么,我应该如何在动作过滤器中获取接口实例?

【问题讨论】:

能否在属性 CustomService 中添加 setter 使其也可写?并在构造函数中添加 ICustomService 作为参数? ASP.Net Core (MVC 6) - Inject service into Action Filter的可能重复 How do I add a parameter to an action filter in asp.net?的可能重复 【参考方案1】:

如果您想避免使用服务定位器模式,您可以通过 TypeFilter 的构造函数注入来使用 DI。

在你的控制器中使用

[TypeFilter(typeof(MyActionFilterAttribute), Arguments = new object[] 10)]
public IActionResult() NiceAction

   ...

您的ActionFilterAttribute 不再需要访问服务提供者实例。

public class MyActionFilterAttribute : ActionFilterAttribute

    public int Limit  get; set;  // some custom parameters passed from Action
    private ICustomService CustomService  get;  // this must be resolved

    public MyActionFilterAttribute(ICustomService service, int limit)
    
        CustomService = service;
        Limit = limit;
    

    public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    
        await next();
    

对我来说注释[TypeFilter(typeof(MyActionFilterAttribute), Arguments = new object[] 10)]似乎很尴尬。为了得到像[MyActionFilter(Limit = 10)]这样更易读的注解,你的过滤器必须继承自TypeFilterAttribute。我对How do I add a parameter to an action filter in asp.net? 的回答展示了这种方法的一个示例。

【讨论】:

如果你需要异步,你也可以使用IAsyncActionFilter而不是IActionFilter【参考方案2】:

你可以使用Service Locator:

public void OnActionExecuting(ActionExecutingContext actionContext)

     var service = actionContext.HttpContext.RequestServices.GetService<IService>();

请注意,泛型方法 GetService&lt;&gt; 是一个扩展方法,位于命名空间 Microsoft.Extensions.DependencyInjection 中。

如果你想使用构造函数注入,请使用TypeFilter。见How do I add a parameter to an action filter in asp.net?

【讨论】:

这是一个很好的建议,但是它需要更多的工作来进行单元测试。 为我工作 - 当我的类 'ApiKeyAuthAttribute : Attribute, IAsyncActionFilter' 使用构造函数注入时,我无法装饰我的控制器 '[ApiKeyAuth(thingToInject)]' 也不能使用 '[ServiceFilter(typeof( ApiKeyAuthAttribute))]',而使用上述方法完全避免构造函数注入并直接从 RequestServices 中获取已解析的 thingToInject 实现。【参考方案3】:

您可以使用 ServiceFilters 在控制器中实例化您需要的 ActionFilters。

在控制器中:

[ServiceFilter(typeof(TrackingAttribute), Order = 2)]

你需要在依赖容器中注册TrackingAttribute,这样ServiceFilter才能解析它。

在https://www.strathweb.com/2015/06/action-filters-service-filters-type-filters-asp-net-5-mvc-6/了解更多信息

【讨论】:

【参考方案4】:

这样做是一个不错的选择(在 .NET Core 3.1 中测试)

    在 Filter 类里面放这个:

    公共静态类FilterContainer

         public class GenericFilter : ActionFilterAttribute
         
             public override void OnActionExecuting(ActionExecutingContext filterContext)
             
                 string Action = filterContext.ActionDescriptor.RouteValues["action"];
                 Console.WriteLine($"[action]: Action STARTING");
             
    
             public override void OnActionExecuted(ActionExecutedContext filterContext)
             
                 string Action = filterContext.ActionDescriptor.RouteValues["action"];
                 Console.WriteLine($"[action]: Action FINISHED");
             
    
             public override void OnResultExecuting(ResultExecutingContext filterContext)
             
                 string Action = filterContext.ActionDescriptor.RouteValues["action"];
                 Console.WriteLine($"[action]: Action GIVING RESULT");
             
    
             public override void OnResultExecuted(ResultExecutedContext filterContext)
             
                 string Action = filterContext.ActionDescriptor.RouteValues["action"];
                 ObjectResult ObjectResult = (ObjectResult)filterContext.Result;
                 Console.WriteLine($"[action]: Action RESULT GIVEN. Value: ObjectResult.Value");
             
         
     
    

    在 Startup.cs/ConfigureServices(IServiceCollection services) 里面放这个:

    services.AddControllers().AddMvcOptions(options => options.Filters.Add(new FilterContainer.GenericFilter()));

结果是,对 .NET Core 应用程序中任何类型的操作的请求都将通过此管道进出,而无需在任何操作之上声明过滤器属性。

让我在 Visual Studio 的“输出”窗口中向您展示一个示例:

[action]: JSON STARTING
[action]: JSON FINISHED
[action]: JSON GIVING RESULT
[action]: JSON RESULT GIVEN. Value: TestId: 103, FullName:...

【讨论】:

以上是关于如何在 ASP.NET CORE 中使用具有依赖注入的动作过滤器?的主要内容,如果未能解决你的问题,请参考以下文章

理解ASP.NET Core

ASP.NET Core Web 应用程序系列- 在ASP.NET Core中使用Autofac替换自带DI进行批量依赖注入(MVC当中应用)

如何在 ASP.NET Core 中转储解析的 JWT 令牌?

如何在 ASP.NET Core MVC 中使用依赖注入设计存储库模式?

ASP.NET Core Web 应用程序系列- 在ASP.NET Core中使用Autofac替换自带DI进行构造函数和属性的批量依赖注入(MVC当中应用)

[ASP.NET Core 3框架揭秘] 依赖注入:一个Mini版的依赖注入框架