如何在 .Net Core ActionFilterAttribute 中使用依赖注入?

Posted

技术标签:

【中文标题】如何在 .Net Core ActionFilterAttribute 中使用依赖注入?【英文标题】:How can I use Dependency Injection in a .Net Core ActionFilterAttribute? 【发布时间】:2019-03-14 11:09:35 【问题描述】:

AuthenticationRequiredAttribute 类

public class AuthenticationRequiredAttribute : ActionFilterAttribute

    ILoginTokenKeyApi _loginTokenKeyApi;
    IMemoryCache _memoryCache;

    public AuthenticationRequiredAttribute(IMemoryCache memoryCache)
    
        _memoryCache = memoryCache;

        _loginTokenKeyApi = new LoginTokenKeyController(new UnitOfWork());
    

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    
        var memory = _memoryCache.Get(Constants.KEYNAME_FOR_AUTHENTICATED_PAGES);

        string requestedPath = filterContext.HttpContext.Request.Path;

        string tokenKey = filterContext.HttpContext.Session.GetString("TokenKey")?.ToString();

        bool? isLoggedIn = _loginTokenKeyApi.IsLoggedInByTokenKey(tokenKey).Data;

        if (isLoggedIn == null ||
            !((bool)isLoggedIn) ||
            !Constants.AUTHENTICATED_PAGES_FOR_NORMAL_USERS.Contains(requestedPath))
        
            filterContext.Result = new JsonResult(new  HttpStatusCode.Unauthorized );
        
    
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    
    

HomeController

public class HomeController : Controller

    IUserApi _userApi;
    ILoginTokenKeyApi _loginTokenKey;
    IMemoryCache _memoryCache;

    public HomeController(IUserApi userApi, ILoginTokenKeyApi loginTokenKey, IMemoryCache memoryCache)
    
        _loginTokenKey = loginTokenKey;
        _userApi = userApi;

        _memoryCache = memoryCache;
    

    [AuthenticationRequired] // There is AN ERROR !!
    public IActionResult Example()
    
        return View();
    

错误:

错误 CS7036 没有给出对应于 所需的形式参数“memoryCache”的 'AuthenticationRequiredAttribute.AuthenticationRequiredAttribute(IMemoryCache)' Project.Ground.WebUI

我的问题实际上是:我不能在属性类中使用依赖注入

我想使用不带任何参数的属性。有什么办法可以解决吗?我使用依赖注入,但它不能用于属性。如何使用它?

【问题讨论】:

How to use dependency injection with an attribute?的可能重复 @Liam 这个问题是关于 ASP.NET MVC 而不是 ASP.NET Core,所以那里的答案并不真正适用。 您今天更改接受的答案有什么原因吗?我在下面的回答重点介绍了解决此问题的三种常见方法,而支出者的回答仅关注其中一种(这通常也被认为是一种反模式)。 【参考方案1】:

根据the documentation,您有几个选择:

如果您的过滤器具有需要从 DI 访问的依赖项,则有几种受支持的方法。您可以使用以下方法之一将过滤器应用于类或操作方法:

ServiceFilterAttribute TypeFilterAttribute IFilterFactory 在您的属性上实现

ServiceFilter 或 TypeFilter 属性

如果您只是想让它快速运行,您可以使用前两个选项之一将过滤器应用到控制器或控制器操作。执行此操作时,您的过滤器本身不需要是属性:

[TypeFilter(typeof(ExampleActionFilter))]
public IActionResult Example()
    => View();

ExampleActionFilter 然后可以实现例如IAsyncActionFilter 可以直接依赖构造函数注入:

public class ExampleActionFilter : IAsyncActionFilter

    private readonly IMemoryCache _memoryCache;
    public ExampleActionFilter(IMemoryCache memoryCache)
    
        _memoryCache = memoryCache;
    

    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
     … 

您也可以使用[ServiceFilter] 属性来获得相同的效果,但是您还需要在Startup 中的依赖注入容器中注册您的ExampleActionFilter

过滤器工厂

如果您需要更大的灵活性,您可以实现自己的过滤器工厂。这允许您编写工厂代码来自己创建实际的过滤器实例。上述ExampleActionFilter 的可能实现如下所示:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ExampleActionFilterAttribute : Attribute, IFilterFactory

    public bool IsReusable => false;

    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    
        return serviceProvider.GetService<ExampleActionFilter>();
    

然后,您可以使用该 [ExampleActionFilter] 属性让 MVC 框架使用 DI 容器为您创建 ExampleActionFilter 的实例。

请注意,此实现与ServiceFilterAttribute 所做的基本相同。只是自己实现它避免了必须直接使用ServiceFilterAttribute 并允许您拥有自己的属性。

使用服务定位器

最后,还有另一个快速选项可以让您完全避免构造函数注入。这使用服务定位器模式在过滤器实际运行时动态解析服务。因此,不是注入依赖项并直接使用它,而是从上下文中显式检索它:

public class ExampleActionFilter : ActionFilterAttribute

    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    
        var memoryCache = context.HttpContext.RequestServices.GetService<IMemoryCache>();

        // …
    

【讨论】:

通用 GetService 方法是一种扩展方法。这意味着你需要有一个 : using Microsoft.Extensions.DependencyInjection;【参考方案2】:

ActionExecutingContext.HttpContext.RequestServices 应该在请求时为您提供对请求的服务容器的引用,而不是在构造时解析。

所以:

public override void OnActionExecuting(ActionExecutingContext filterContext)

    var svc = filterContext.HttpContext.RequestServices;
    var memCache = svc.GetService<IMemoryCache>();
    //..etc

【讨论】:

使用 Microsoft.Extensions.DependencyInjection; 在动作过滤器中使用 Memcache 并尝试获取它总是返回 null 的值【参考方案3】:

对于 .Net Core 5,以下语法对我有用。

 IAppUserService _appUserService = (IAppUserService)context.HttpContext.RequestServices.GetService(typeof(IAppUserService));

【讨论】:

以上是关于如何在 .Net Core ActionFilterAttribute 中使用依赖注入?的主要内容,如果未能解决你的问题,请参考以下文章

如何在ubuntu安装.net core sdk

ASP.NET Core中的缓存[1]:如何在一个ASP.NET Core应用中使用缓存

如何在 Visual Studio 中将 .NET Framework 更改为 .NET Standard/Core?

如何使用 EF Core 在 ASP.NET Core 中取消应用迁移

如何将 .NET Core 2.2 Web API 迁移到 .NET Core 3.0?

如何在 ASP.Net Core 中使用 IHostedService