Abhängigkeitsinjektion mit Attributen auf der .NET-API

· 2 Min. Lesezeit

Abhängigkeitsinjektion ist derzeit wahrscheinlich eine der besten Funktionen, die wir in .NET haben. Es gibt keine Möglichkeit, dass Sie es nicht verwenden. Wenn Sie also wie ich sind, möchten Sie es unbedingt zu allen Implementierungen hinzufügen, die Sie vornehmen.

Filter gemäß der offiziellen [Dokumentation] von Microsoft (https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-3.1):

Filter in ASP.NET Core ermöglichen die Ausführung von Code vor oder nach bestimmten Phasen in der Anforderungsverarbeitungspipeline.

Eingebaute Filter erledigen Aufgaben wie:

  • Autorisierung, die den Zugriff auf Ressourcen verhindert, für die ein Benutzer keine Autorisierung hat.
  • Antwort-Caching, Kurzschließen der Anforderungspipeline, um eine zwischengespeicherte Antwort zurückzugeben.

Benutzerdefinierte Filter können erstellt werden, um übergreifende Anliegen zu berücksichtigen. Beispiele für übergreifende Anliegen sind Fehlerbehandlung, Caching, Konfiguration, Autorisierung und Protokollierung. Filter vermeiden die Duplizierung von Code.

Ich arbeite viel mit APIs und es gibt einige Dinge, die jede einzelne Anfrage oder so ziemlich alle ausführen müssen. Idealerweise möchten wir damit arbeiten und außerdem … Abhängigkeitsinjektion!

Aber manchmal ist es ein kleiner Trick, es funktioniert nicht wie gewünscht, wenn wir von ActionAttribute erben wollen, also müssen wir mit TypeFilterAttribute arbeiten, was uns Dinge beim Überschreiben von OnActionExecutionAsync ermöglichen lässt.

Normalerweise erstelle ich diese Filter, um etwas zu protokollieren, also verwenden wir das als Beispiel:

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

Die Logik ist ziemlich einfach. Wir erhalten den Körper, indem wir mit context.ActionArguments.First().Value auf das Objekt context zugreifen, außerdem erhalten wir den Methodenaufruf mit context.HttpContext.Request.Path.Value.

Dann rufen wir einfach unsere Methode aus unserem Dienst auf, in diesem Fall _loggingService.LogCustomEvent(call).

Dann müssen wir await next(); aufrufen, da die Pipeline fortgesetzt werden muss.

Dies gilt für das Attribut. Jetzt müssen wir dieses Attribut tatsächlich in eine Methode einbinden.

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

Ich hoffe, es hat Ihnen gefallen. Wenn Sie Fragen haben oder Kontakt mit mir aufnehmen möchten, zögern Sie nicht und kontaktieren Sie mich!