Применение сервисов в классах middleware

Последнее обновление: 17.05.2022

После добавления сервисов в коллекцию Services объекта WebApplicationBuilder они становятся доступны приложению, в том числе и в кастомных компонентах middleware. В middleware мы можем получить зависимости тремя способами:

  • Через конструктор

  • Через параметр метода Invoke/InvokeAsync

  • Через свойство HttpContext.RequestServices

При этом надо учитывать, что компоненты middleware создаются при запуске приложения и живут в течение всего жизненного цикла приложения. То есть при последующих запросах инфраструктура ASP.NET Core использует ранее созданный компонент. И это налагает ограничения на использование зависимостей в middleware.

В частности, если конструктор передается transient-сервис, который создается при каждом обращении к нему, то при последующих запросах мы будем использовать тот же самый сервис, так как конструктор middleware вызывается один раз - при создании приложения.

Например, определим сервис TimeService:

Public Class TimeService
    Public ReadOnly Property Time = Now.ToLongTimeString()
End Class

Его свойство Time получает текущее время в виде строки.

Добавим новый компонент TimerMiddleware, который будет использовать этот сервис для вывода времени на веб-страницу:

Imports Microsoft.AspNetCore.Http

Public Class TimerMiddleware
    Dim service As TimeService
    Dim nextMiddleware As RequestDelegate
    Public Sub New(nextMiddleware As RequestDelegate, service As TimeService)
        Me.service = service
        Me.nextMiddleware = nextMiddleware
    End Sub

    Public Async Function InvokeAsync(context As HttpContext) As Task

        If context.Request.Path.Value = "/time" Then
            Await context.Response.WriteAsync($"Текущее время: {service.Time}")
        Else
            Await nextMiddleware.Invoke(context)
        End If
    End Function
End Class

Если сервис TimeService добавляется в коллекцию сервисов приложения, то мы сможем получить его через конструктор класса TimerMiddleware.

Логика компонента предполагает, что, если запрос пришел по адресу "/time", то с помощью TimeService возвращается текущее время. Иначе мы просто обращаемся к следующему middleware в конвейере обработки запроса.

Используем этот компонент в файле Program.vb:

Imports Microsoft.AspNetCore.Builder
Imports Microsoft.AspNetCore.Http
Imports Microsoft.Extensions.DependencyInjection

Module Program
    Sub Main(args As String())

        Dim builder = WebApplication.CreateBuilder(args)
        builder.Services.AddTransient(Of TimeService)()

        Dim app = builder.Build()

        app.UseMiddleware(Of TimerMiddleware)()
        app.Run(Async Function(context As HttpContext) As Task
                    Await context.Response.WriteAsync("Hello METANIT.COM")
                End Function)
        app.Run()
    End Sub
End Module

В итоге, если мы обратимся по пути "/time", то приложение выведет текущее время:

Сервисы в классах middleware в ASP.NET Core и Visual Basic .NET

Однако сколько бы мы раз не обращались по этому пути, мы все время будем получать одно и то же время, так как объект TimerMiddleware был создан еще при первом запросе. Поэтому передача через конструктор middleware больше подходит для сервисов с жизненным циклом Singleton, которые создаются один раз для всех последующих запросов.

Если же в middleware необходимо использовать сервисы с жизненным циклом Scoped или Transient, то лучше их передавать через параметр метода Invoke/InvokeAsync:

Imports Microsoft.AspNetCore.Http

Public Class TimerMiddleware
    Dim nextMiddleware As RequestDelegate
    Public Sub New(nextMiddleware As RequestDelegate)
        Me.nextMiddleware = nextMiddleware
    End Sub

    Public Async Function InvokeAsync(context As HttpContext, service As TimeService) As Task

        If context.Request.Path.Value = "/time" Then
            Await context.Response.WriteAsync($"Текущее время: {service.Time}")
        Else
            Await nextMiddleware.Invoke(context)
        End If
    End Function
End Class

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850