将服务注入Action Filter

时间:2016-03-20 00:47:58

标签: c# dependency-injection asp.net-core

我正在尝试将一个服务注入我的动作过滤器,但我没有在构造函数中注入所需的服务。这就是我所拥有的:

public class EnsureUserLoggedIn : ActionFilterAttribute
{
    private readonly ISessionService _sessionService;

    public EnsureUserLoggedIn()
    {
        // I was unable able to remove the default ctor 
        // because of compilation error while using the 
        // attribute in my controller
    }

    public EnsureUserLoggedIn(ISessionService sessionService)
    {
        _sessionService = sessionService;
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        // Problem: _sessionService is null here
        if (_sessionService.LoggedInUser == null)
        {
            context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
            context.Result = new JsonResult("Unauthorized");
        }
    }
}

我正在装饰我的控制器:

[Route("api/issues"), EnsureUserLoggedIn]
public class IssueController : Controller
{
}

Startup.cs

services.AddScoped<ISessionService, SessionService>();

6 个答案:

答案 0 :(得分:59)

使用这些文章作为参考:

ASP.NET Core Action Filters

Action filters, service filters and type filters in ASP.NET 5 and MVC 6

将过滤器用作ServiceFilter

由于过滤器将用作ServiceType,因此需要在框架IoC中注册。如果直接使用动作过滤器,则不需要这样做。

Startup.cs

public void ConfigureServices(IServiceCollection services) {
    services.AddMvc();

    services.AddScoped<ISessionService, SessionService>();
    services.AddScoped<EnsureUserLoggedIn>();

    ...
}

自定义过滤器使用ServiceFilter属性添加到MVC控制器方法和控制器类中,如下所示:

[ServiceFilter(typeof(EnsureUserLoggedIn))]
[Route("api/issues")]
public class IssueController : Controller {
    // GET: api/issues
    [HttpGet]
    [ServiceFilter(typeof(EnsureUserLoggedIn))]
    public IEnumerable<string> Get(){...}
}

还有其他

的例子
  • 将过滤器用作全局过滤器

  • 将过滤器与基本控制器配合使用

  • 使用带订单的过滤器

看一看,试一试,看看是否可以解决您的问题。

希望这有帮助。

答案 1 :(得分:28)

全局过滤器

您需要实施IFilterFactory

public class AuthorizationFilterFactory : IFilterFactory
{
    public bool IsReusable => false;

    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
        // manually find and inject necessary dependencies.
        var context = (IMyContext)serviceProvider.GetService(typeof(IMyContext));
        return new AuthorizationFilter(context);
    }
}

Startup类而不是注册实际过滤器,您注册过滤器工厂:

services.AddMvc(options =>
{
    options.Filters.Add(new AuthorizationFilterFactory());
});

答案 2 :(得分:25)

解决此问题的另一种方法。您可以通过上下文获取服务,如下面的代码所示:

public override void OnActionExecuting(ActionExecutingContext context)
{
    _sessionService = context.HttpContext.RequestServices.GetService<ISessionService>();
    if (_sessionService.LoggedInUser == null)
    {
        context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
        context.Result = new JsonResult("Unauthorized");
    }
}

请注意,您必须在Startup.cs

中注册此服务
services.AddTransient<ISessionService, SessionService>();

答案 3 :(得分:8)

阅读完本文ASP.NET Core - Real-World ASP.NET Core MVC Filters (Aug 2016)后,我实现了这样:

在Starup.cs / ConfigureServices中:

services.AddScoped<MyService>();

在MyFilterAttribute.cs中:

public class MyFilterAttribute : TypeFilterAttribute
{        
    public MyFilterAttribute() : base(typeof (MyFilterAttributeImpl))
    {

    }

    private class MyFilterAttributeImpl : IActionFilter
    {
        private readonly MyService _sv;

        public MyFilterAttributeImpl(MyService sv)
        {
            _sv = sv;
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {                
            _sv.MyServiceMethod1();
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            _sv.MyServiceMethod2();
        }
    }
}

在MyFooController.cs中:

[MyFilter]
public IActionResult MyAction()
{
}

编辑:传递像[MyFilter("Something")]这样的参数可以使用TypeFilterAttribute类的Arguments属性来完成:How do I add a parameter to an action filter in asp.net?(rboe的代码也显示了如何注入事物(以相同的方式))

答案 4 :(得分:2)

示例

private ILoginService _loginService;

public override void OnActionExecuting(ActionExecutingContext context)
        {
            _loginService = (ILoginService)context.HttpContext.RequestServices.GetService(typeof(ILoginService));
        }

希望有帮助。

答案 5 :(得分:0)

虽然问题隐式涉及“通过属性过滤器”,但仍然值得强调的是,“按类型全局”添加过滤器支持即用型DI:

  

[对于按类型添加的全局过滤器] 任何构造函数依赖项都将通过依赖项注入(DI)填充。按类型添加过滤器等效于filter.Add(new TypeFilterAttribute(typeof(MyFilter)))。   https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2#dependency-injection

关于基于属性的过滤器:

  

作为属性实现并直接添加到控制器类或操作方法的筛选器不能具有依赖项注入(DI)提供的构造函数依赖项。这是因为属性必须在应用位置提供其构造函数参数。这是属性工作方式的限制。   https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2#dependency-injection

但是,正如前面对OP的回答中所述,有一些间接方法可用于实现DI。为了完整起见,以下是官方文档的链接: