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

时间:2018-10-09 15:54:23

标签: c# asp.net .net asp.net-core memorycache

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

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

我想不带任何参数使用该属性。有解决方案吗?我使用依赖注入,但不能用于属性。我如何使用它?

2 个答案:

答案 0 :(得分:6)

根据the documentation,您可以在此处选择以下几种方式:

  

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

     

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]属性来获得相同的效果,但是您还需要在ExampleActionFilter中的依赖项注入容器中注册Startup

过滤器工厂

如果需要更大的灵活性,则可以实现自己的过滤器工厂。这使您可以编写工厂代码来自己创建实际的过滤器实例。上述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>();

        // …
    }
}

答案 1 :(得分:4)

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

所以:

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
    var svc = filterContext.HttpContext.RequestServices;
    var memCache = svc.GetService<IMemoryCache>();
    //..etc