.NET API の属性による依存関係の注入

· 1分で読める

依存関係の注入は、おそらく現時点で .NET にある最高の機能の 1 つです。どのような場合でも、これを使用しないわけはありません。そのため、あなたが私と同じであれば、作成するすべての実装にそれを追加したいと思うでしょう。

公式 Microsoft の ドキュメント によるフィルター:

ASP.NET Core のフィルターを使用すると、要求処理パイプラインの特定のステージの前後でコードを実行できます。

組み込みフィルターは次のようなタスクを処理します。

  • 認可。ユーザーが認可されていないリソースへのアクセスを防止します。
  • 応答キャッシュ。要求パイプラインを短絡して、キャッシュされた応答を返します。

横断的な問題に対処するためにカスタム フィルターを作成できます。横断的な問題の例としては、エラー処理、キャッシュ、構成、認可、ロギングなどが挙げられます。フィルターはコードの重複を避けます。

私は API をよく使うのですが、すべてのリクエスト、またはほぼすべてのリクエストを実行する必要があるものがいくつかあります。そのため、理想的には API に加えて依存関係の注入を実行したいと考えています。

しかし、これは時々ちょっとしたトリックです。ActionAttribute から継承したい場合は希望どおりに動作しないため、TypeFilterAttribute を操作する必要があります。これにより、OnActionExecutionAsync をオーバーライドするときに処理を行うことができます。

通常、ログを記録するためにこのフィルターを作成するので、それを例として使用します。

/// <summary>
/// LoggedQueryAttribute class
/// </summary>
public class LoggedQueryTypeFilterAttribute : TypeFilterAttribute
{
    /// <summary>
    /// Constructor for <see cref="LoggedQueryTypeFilterAttribute"/>
    /// </summary>
    public LoggedQueryTypeFilterAttribute() : base(typeof(LoggedQueryFilter))
    {
    }

    /// <summary>
    /// LoggedQueryFilter class
    /// </summary>
    private class LoggedQueryFilter : IAsyncActionFilter
    {
        /// <summary>
        /// <see cref="_loggingService"/> object
        /// </summary>
        private readonly LoggingService _loggingService;

        /// <summary>
        /// Constructor for <see cref="LoggedQueryFilter"/>
        /// </summary>
        /// <param cref="LoggingService" name="loggingService">Parameter for loggingService</param>
        public LoggedQueryFilter(LoggingService loggingService)
        {
            _loggingService = loggingService;
        }

        /// <summary>
        /// OnActionExecutionAsync
        /// </summary>
        /// <param cref="ActionExecutingContext" name="context">Parameter for context</param>
        /// <param cref="ActionExecutionDelegate" name="next">Parameter for next</param>
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            // Get properties
            var properties = (Request)context.ActionArguments.First().Value!;

            // Get call from context
            var call = context.HttpContext.Request.Path.Value!;

            // Logging
            _loggingService.LogCustomEvent(call);

            // Continue call
            await next();
        }
    }
}

ロジックは非常に単純で、context.ActionArguments.First().Valuecontext オブジェクトにアクセスして本体を取得し、context.HttpContext.Request.Path.Value でメソッド呼び出しも取得します。

次に、サービスからメソッドを呼び出すだけです。この場合は _loggingService.LogCustomEvent(call) です。

次に、パイプラインを続行する必要があるため、await next(); を呼び出す必要があります。

これは属性に関するものですが、この属性を実際にメソッドに含める必要があります。

[LoggedQueryTypeFilterAttribute]
public ActionResult<string> TestFilter()
{
    return Ok("Hello world!");
}

気に入っていただければ幸いです。ご質問がある場合、または連絡したい場合は、ためらわずにご連絡ください。