web-dev-qa-db-ja.com

アクションフィルターにサービスを挿入する

アクションフィルターにサービスを挿入しようとしていますが、必要なサービスがコンストラクターに挿入されていません。ここに私が持っているものがあります:

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

これらの記事を参照として使用:

ASP.NET Core Action Filters

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

他の例がありました

  • フィルターをグローバルフィルターとして使用する

  • ベースコントローラーでフィルターを使用する

  • 注文でフィルターを使用する

見て、試してみて、問題が解決するかどうかを確認してください。

お役に立てれば。

65
Nkosi

グローバルフィルター

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());
});
28
Andrei

この問題を解決するもう1つの方法。次のコードのように、コンテキストを介してサービスを取得できます。

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>();
25
Igor Valikovsky

この記事を読んだ後 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プロパティを使用します。 asp.netでアクションフィルターにパラメーターを追加する方法 物を注入する(同じ方法))

8
A.J.Bauer

private ILoginService _loginService;

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

それが役に立てば幸い。

3
Bukunmi

質問は暗黙的に「属性を介したフィルター」に言及していますが、「タイプ別にグローバルに」フィルターを追加すると、すぐに使用できるDIがサポートされることを強調する価値があります。

[タイプ別に追加されたグローバルフィルターの場合]すべてのコンストラクター依存関係は、依存関係注入(DI)によって設定されます。タイプによるフィルターの追加は、filters.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を達成するために使用できる間接化の方法があります。完全を期すため、公式ドキュメントへのリンクは次のとおりです。

0
B12Toaster