Как правило, в конвейер обработки запроса входит не один, а несколько компонентов 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, то увидим искомый текст:
Добавим еще один компонент 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
Схематично конвейер обработки запроса будет выглядеть следующим образом:
В то же время, если к приложению обратится пользователь, не указав в строке запроса параметр token, то AuthenticationMiddleware не будет передавать дальше запрос на обработку, а конвейер обработки будет выглядеть так:
В первом случае, если указан параметр token, то запрос будет обработан RoutingMiddleware:
Иначе пользователь получит ошибку 403: