将服务注入 Action Filter
Inject service into Action Filter
我正在尝试将服务注入到我的操作过滤器中,但我没有在构造函数中注入所需的服务。这是我拥有的:
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>();
使用这些文章作为参考:
Action filters, service filters and type filters in ASP.NET 5 and MVC 6
将过滤器用作服务过滤器
因为过滤器将被用作ServiceType
,所以需要向框架IoC注册。如果直接使用动作过滤器,则不需要。
Startup.cs
public void ConfigureServices(IServiceCollection services) {
services.AddMvc();
services.AddScoped<ISessionService, SessionService>();
services.AddScoped<EnsureUserLoggedIn>();
...
}
自定义过滤器添加到 MVC 控制器方法和控制器 class 使用 ServiceFilter
属性,如下所示:
[ServiceFilter(typeof(EnsureUserLoggedIn))]
[Route("api/issues")]
public class IssueController : Controller {
// GET: api/issues
[HttpGet]
[ServiceFilter(typeof(EnsureUserLoggedIn))]
public IEnumerable<string> Get(){...}
}
还有其他例子
将过滤器用作全局过滤器
将过滤器与基本控制器一起使用
在订单中使用过滤器
看看,试一试,看看是否能解决您的问题。
希望这对您有所帮助。
全局过滤器
您需要实施 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
class 中,不是注册实际的过滤器,而是注册过滤器工厂:
services.AddMvc(options =>
{
options.Filters.Add(new AuthorizationFilterFactory());
});
解决此问题的另一种方法。您可以通过 Context 获取服务,如下代码所示:
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>();
看完这篇文章后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()
{
}
编辑:可以使用 TypeFilterAttribute class 的参数 属性 来传递像 [MyFilter("Something")]
这样的参数:(rboe 的代码还展示了如何注入东西(同理))
虽然问题隐含地提到了 "filters via attributes",但仍然值得强调的是添加过滤器 "globally by type" 支持开箱即用的 DI:
[For global filters added by type] any constructor dependencies will be populated by dependency injection (DI). Adding a filter by type is equivalent to filters.Add(new TypeFilterAttribute(typeof(MyFilter))).
https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2#dependency-injection
关于基于属性的过滤器:
Filters that are implemented as attributes and added directly to controller classes or action methods cannot have constructor dependencies provided by dependency injection (DI). This is because attributes must have their constructor parameters supplied where they're applied. This is a limitation of how attributes work.
https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2#dependency-injection
但是,正如之前对OP的回答中提到的,有一些间接的方式可以用来实现DI。为了完整起见,这里是官方文档的链接:
示例
private ILoginService _loginService;
public override void OnActionExecuting(ActionExecutingContext context)
{
_loginService = (ILoginService)context.HttpContext.RequestServices.GetService(typeof(ILoginService));
}
希望对您有所帮助。
我正在尝试将服务注入到我的操作过滤器中,但我没有在构造函数中注入所需的服务。这是我拥有的:
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>();
使用这些文章作为参考:
Action filters, service filters and type filters in ASP.NET 5 and MVC 6
将过滤器用作服务过滤器
因为过滤器将被用作ServiceType
,所以需要向框架IoC注册。如果直接使用动作过滤器,则不需要。
Startup.cs
public void ConfigureServices(IServiceCollection services) {
services.AddMvc();
services.AddScoped<ISessionService, SessionService>();
services.AddScoped<EnsureUserLoggedIn>();
...
}
自定义过滤器添加到 MVC 控制器方法和控制器 class 使用 ServiceFilter
属性,如下所示:
[ServiceFilter(typeof(EnsureUserLoggedIn))]
[Route("api/issues")]
public class IssueController : Controller {
// GET: api/issues
[HttpGet]
[ServiceFilter(typeof(EnsureUserLoggedIn))]
public IEnumerable<string> Get(){...}
}
还有其他例子
将过滤器用作全局过滤器
将过滤器与基本控制器一起使用
在订单中使用过滤器
看看,试一试,看看是否能解决您的问题。
希望这对您有所帮助。
全局过滤器
您需要实施 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
class 中,不是注册实际的过滤器,而是注册过滤器工厂:
services.AddMvc(options =>
{
options.Filters.Add(new AuthorizationFilterFactory());
});
解决此问题的另一种方法。您可以通过 Context 获取服务,如下代码所示:
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>();
看完这篇文章后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()
{
}
编辑:可以使用 TypeFilterAttribute class 的参数 属性 来传递像 [MyFilter("Something")]
这样的参数:
虽然问题隐含地提到了 "filters via attributes",但仍然值得强调的是添加过滤器 "globally by type" 支持开箱即用的 DI:
[For global filters added by type] any constructor dependencies will be populated by dependency injection (DI). Adding a filter by type is equivalent to filters.Add(new TypeFilterAttribute(typeof(MyFilter))). https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2#dependency-injection
关于基于属性的过滤器:
Filters that are implemented as attributes and added directly to controller classes or action methods cannot have constructor dependencies provided by dependency injection (DI). This is because attributes must have their constructor parameters supplied where they're applied. This is a limitation of how attributes work. https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2#dependency-injection
但是,正如之前对OP的回答中提到的,有一些间接的方式可以用来实现DI。为了完整起见,这里是官方文档的链接:
示例
private ILoginService _loginService;
public override void OnActionExecuting(ActionExecutingContext context)
{
_loginService = (ILoginService)context.HttpContext.RequestServices.GetService(typeof(ILoginService));
}
希望对您有所帮助。