programing

액션 필터에 서비스 삽입

procenter 2021. 1. 17. 12:08
반응형

액션 필터에 서비스 삽입


내 작업 필터에 서비스를 주입하려고하는데 생성자에 필요한 서비스가 주입되지 않습니다. 내가 가진 것은 다음과 같습니다.

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>();

이 기사를 참조로 사용 :

ASP.NET Core 작업 필터

ASP.NET 5 및 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(){...}
}

다른 예가있었습니다.

  • 필터를 전역 필터로 사용

  • 기본 컨트롤러와 함께 필터 사용

  • 주문과 함께 필터 사용

한 번 살펴보고 시도해보고 문제가 해결되는지 확인하세요.

도움이 되었기를 바랍니다.


글로벌 필터

다음을 구현해야합니다 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");
    }
}

이 서비스는 Startup.cs에 등록해야합니다.

services.AddTransient<ISessionService, SessionService>();

이 기사 ASP.NET Core-Real-World ASP.NET Core MVC 필터 (2016 년 8 월)를 읽은 후 다음과 같이 구현했습니다.

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()
{
}

Edit: Passing arguments like [MyFilter("Something")] can be done using the Arguments property of the TypeFilterAttribute class: How do I add a parameter to an action filter in asp.net? (rboe's code also shows how to inject things (the same way))


Example

private ILoginService _loginService;

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

Hope it helps.


While the question implicitly refers to "filters via attributes", it is still worth highlighting that adding filters "globally by type" supports DI out-of-the-box:

[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

With regards to attribute-based filters:

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

However, as mentioned in the previous answers to the OP, there are ways of indirection that can be used to achieve DI. For the sake of completeness, here are the links to the official docs:

ReferenceURL : https://stackoverflow.com/questions/36109052/inject-service-into-action-filter

반응형