Построение конвейера обработки запроса

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

Как правило, в конвейер обработки запроса входит не один, а несколько компонентов middleware. И в этом случае большое значение может играть порядок помещения компонентов в конвейер, а также то, как они взаимодействуют с другими компонентами.

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

Рассмотрим простейший пример. Для обработки запроса определим в проекте следующий класс RoutingMiddleware:

Imports Microsoft.AspNetCore.Http

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

    Public Async Function InvokeAsync(context As HttpContext) As Task
        Dim path = context.Request.Path.Value
        If path = "/index" Then
            Await context.Response.WriteAsync("Index Page")
        ElseIf path = "/about" Then
            Await context.Response.WriteAsync("About Page")
        Else
            context.Response.StatusCode = 404
            Await context.Response.WriteAsync("Not Found")
        End If
    End Function
End Class

Этот компонент в зависимости от строки запроса возвращает либо определенную строку, либо устанавливает код ошибки.

Допустим, мы хотим, чтобы пользователь был аутентифицирован при обращении к нашему приложению. Для этого добавим новый класс, который назовем AuthenticationMiddleware:

Imports Microsoft.AspNetCore.Http
Public Class AuthenticationMiddleware
    Dim nextMiddleware As RequestDelegate
    Public Sub New(nextMiddleware As RequestDelegate)
        Me.nextMiddleware = nextMiddleware
    End Sub

    Public Async Function InvokeAsync(context As HttpContext) As Task
        Dim token = context.Request.Query("token")
        If String.IsNullOrWhiteSpace(token) Then
            context.Response.StatusCode = 403
        Else
            Await nextMiddleware.Invoke(context)
        End If
    End Function
End Class

Условно будем считать, что если в строке запроса есть параметр token и он имеет какое-нибудь значение, то пользователь аутентифицирован. А если он не аутентифицирован, то надо необходимо ограничить доступ пользователям к приложению. Если пользователь не аутентифицирован, то устанавливаем статусный код 403, иначе передаем выполнение запроса следующему в конвейере делегату.

Поскольку компоненту RoutingMiddleware нет смысла обрабатывать запрос, если пользователь не аутентифицирован, то в конвейере компонент AuthenticationMiddleware должен быть помещен перед компонентом RoutingMiddleware:

Imports Microsoft.AspNetCore.Builder
Module Program
    Sub Main(args As String())

        Dim builder = WebApplication.CreateBuilder(args)
        Dim app = builder.Build()

        app.UseMiddleware(Of AuthenticationMiddleware)
        app.UseMiddleware(Of RoutingMiddleware)

        app.Run()
    End Sub
End Module

Таким образом, если мы сейчас запустим проект и обратимся по пути "/index" или "/about" и не передадим параметр token, то мы получим ошибку. Если же обратимся по пути /index или /about и передадим значение параметра token, то увидим искомый текст:

Организация pipeline в ASP.NET Core и Visual Basic .NET

Добавим еще один компонент middleware, который назовем ErrorHandlingMiddleware:

Imports Microsoft.AspNetCore.Http

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

    Public Async Function InvokeAsync(context As HttpContext) As Task

        Await nextMiddleware.Invoke(context)

        If context.Response.StatusCode = 403 Then
            Await context.Response.WriteAsync("Access Denied")
        ElseIf context.Response.StatusCode = 404 Then
            Await context.Response.WriteAsync("Not Found")
        End If

    End Function
End Class

В отличие от предыдущих двух компонентов ErrorHandlingMiddleware сначала передает запрос на выполнение последующим делегатам, а потом уже сам обрабатывает. Это возможно, поскольку каждый компонент обрабатывает запрос два раза: вначале вызывается та часть кода, которая идет до Await nextMiddleware.Invoke(context), а после завершения обработки последующих компонентов вызывается та часть кода, которая идет после Await nextMiddleware.Invoke(context). И в данном случае для ErrorHandlingMiddleware важен результат обработки запроса последующими компонентами. В частности, он устанавливает сообщения об ошибках в зависимости от того, как статусный код установили другие компоненты. Поэтому ErrorHandlingMiddleware должен быть помещен первым из всех трех компонентов:

Imports Microsoft.AspNetCore.Builder
Module Program
    Sub Main(args As String())

        Dim builder = WebApplication.CreateBuilder(args)
        Dim app = builder.Build()

        app.UseMiddleware(Of ErrorHandlingMiddleware)
        app.UseMiddleware(Of AuthenticationMiddleware)
        app.UseMiddleware(Of RoutingMiddleware)

        app.Run()
    End Sub
End Module

Схематично конвейер обработки запроса будет выглядеть следующим образом:

Конвейер обработки запроса в ASP.NET Core и Visual Basic .NET

В то же время, если к приложению обратится пользователь, не указав в строке запроса параметр token, то AuthenticationMiddleware не будет передавать дальше запрос на обработку, а конвейер обработки будет выглядеть так:

порядок обработки запроса в Middleware в ASP.NET Core и Visual Basic .NET

В первом случае, если указан параметр token, то запрос будет обработан RoutingMiddleware:

Этапы обработки запроса в ASP.NET Core и Visual Basic .NET

Иначе пользователь получит ошибку 403:

Цикл обработки запроса в ASP.NET Core и Visual Basic .NET
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850