C# 将服务注入操作过滤器

C# 将服务注入操作过滤器,c#,asp.net-core,dependency-injection,.net-core,C#,Asp.net Core,Dependency Injection,.net Core,我正试图将服务注入到我的操作过滤器中,但在构造函数中没有注入所需的服务。以下是我所拥有的: public class EnsureUserLoggedIn : ActionFilterAttribute { private readonly ISessionService _sessionService; public EnsureUserLoggedIn() { // I was unable able to remove the default ct

我正试图将服务注入到我的操作过滤器中,但在构造函数中没有注入所需的服务。以下是我所拥有的:

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>();
public void ConfigureServices(IServiceCollection services) {
    services.AddMvc();

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

    ...
}
services.AddTransient<ISessionService, SessionService>();
services.addScope();

使用这些文章作为参考:

将筛选器用作服务筛选器

由于筛选器将用作
服务类型
,因此需要向框架IoC注册。如果直接使用动作过滤器,则不需要这样做

Startup.cs

services.AddScoped<ISessionService, SessionService>();
public void ConfigureServices(IServiceCollection services) {
    services.AddMvc();

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

    ...
}
services.AddTransient<ISessionService, SessionService>();
还有其他的例子

  • 将筛选器用作全局筛选器

  • 将过滤器与基本控制器一起使用

  • 使用带顺序的过滤器

看看,给他们一个尝试,看看这是否解决了你的问题

希望这有帮助。

全局过滤器 您需要实现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());
});

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

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");
    }
}
public override void OnActionExecuting(ActionExecutingContext上下文)
{
_sessionService=context.HttpContext.RequestServices.GetService();
if(_sessionService.LoggedInUser==null)
{
context.HttpContext.Response.StatusCode=(int)HttpStatusCode.Unauthorized;
context.Result=新的JsonResult(“未经授权”);
}
}
请注意,您必须在Startup.cs中注册此服务

services.AddScoped<ISessionService, SessionService>();
public void ConfigureServices(IServiceCollection services) {
    services.AddMvc();

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

    ...
}
services.AddTransient<ISessionService, SessionService>();
services.AddTransient();

阅读本文后,我实现了如下:

在Starup.cs/ConfigureServices中:

services.AddScoped<MyService>();
在MyFooController.cs中:

[MyFilter]
public IActionResult MyAction()
{
}

编辑:可以使用TypeFilterAttribute类的arguments属性来传递参数,如
[MyFilter(“Something”)]
,(rboe的代码还显示了如何注入内容(以相同的方式))

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

[对于按类型添加的全局筛选器]任何构造函数依赖项都将由依赖项注入(DI)填充。按类型添加筛选器相当于添加筛选器。添加(new-TypeFilterAttribute(typeof(MyFilter)))。

关于基于属性的过滤器:

作为属性实现并直接添加到控制器类或操作方法的筛选器不能具有依赖项注入(DI)提供的构造函数依赖项。这是因为属性必须在应用它们的地方提供它们的构造函数参数。这是属性工作方式的限制。

然而,正如前面对OP的回答中提到的,有一些间接方法可以用来实现DI。为完整起见,以下是官方文件的链接:

示例

private ILoginService _loginService;

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

希望有帮助。

属性装饰只允许常量值。我想你应该在默认构造函数中解析你的服务。我不确定在这种情况下这是怎么可能的。看看这里,我想你只需要一个,而不是试图将服务注入属性中,写入。你不应该为授权/策略实现你自己的筛选器或授权属性。这就是
AuthoirzeAttribute
的策略生成器和策略属性的作用。从一位负责ASP.NET核心安全部分的ASP.NET开发人员那里可以看到这个答案是的,这很有效。我以前不熟悉ServiceFilter属性。这是我代码中缺少的部分。谢谢。你不应该用这种方式进行授权策略检查,这不是我们想要的方式。您应该使用Cookie授权或其他授权类型(例如jwt),然后使用
AuthorizeAttribute
和您在应用程序启动时设置的策略。检查我上面的评论有趣的是,全局筛选器似乎不允许DI,请参阅:-博客作者实际上在那里硬连接了依赖项。找到了一个实际上涵盖全局筛选器+依赖项注入的博客:如果需要在属性上指定属性,这如何工作?谢谢!这不是一个显而易见的解决方案,我自己很难找到。@StephenM.Redd确保它不可重复使用。否则它将使用已释放的上下文。我无法使用GetService。它告诉我使用Microsoft.Extensions.DependencyInjection.ServerProviderServiceExtensions.GetService,但我似乎找不到可用的解决方案。这似乎是对我进行此小调整的最佳解决方案:var service=context.HttpContext.RequestServices.GetService(typeof(IMyService))作为IMyService;这可能会起作用,但它使用的是服务定位器模式,有时被认为是反模式的简单示例,但很简单。就是我要找的那个。通过使用(T)context.HttpContext.RequestServices.GetService(typeof(T))解析依赖关系。干得好