Метод Use

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

Метод расширения Use() добавляет компонент middleware, который позволяет передать обработку запроса далее следующим в конвейере компонентам. Этот метод реализован как метод расширения для типа IApplicationBuilder, соответственно мы можем вызвать данный метод у объекта WebApplication для добавления middleware в приложение.

Метод Use() имеет следующие версии

Use(middleware As Func(Of HttpContext, Func(Of Task), Task))
Use(middleware As Func(Of HttpContext, RequestDelegate, Task))

В обоих версиях метод Use принимает некоторое действие, которое имеет два параметра и возвращает объект Task.

Первый параметр делегата Func, который передается в метод Use(), представляет объект HttpContext. Этот объект позволяет получить данные запроса и управлять ответом клиенту.

Второй параметр делегата представляет другой делегат - Func(Of Task) или RequestDelegate. Этот делегат представляет следующий в конвейере компонент middleware, которому будет передаваться обработка запроса.

В общем случае применение метода Use() выглядит следующим образом:

app.Use(Async Function(context As HttpContext, nextMiddleware As Func(Of Task)) As Task
            'действия перед передачи запроса в следующий middleware
            Await nextMiddleware.Invoke()
            'действия после обработки запроса следующим middleware
    End Function)

Работа middleware разбивается на две части:

  • Middleware выполняет некоторую начальную обработку запроса до вызова Await nextMiddleware.Invoke()

  • Затем вызывается метод nextMiddleware.Invoke(), который передает обработку запроса следующему компоненту в конвейере

  • Когда следующий в конвейере компонент закончил обработку запрос возвращается в обратно в текущий компонент, и выполняются действия, которые идут после вызова await nextMiddleware.Invoke(

Таким образом, middleware в методе Use выполняет действия до следующего в конвейере компонента и после него.

Рассмотрим метод Use() на примере:

var builder = WebApplication.CreateBuilder();
var app = builder.Build();

string date = "";

app.Use(async(context, next) =>
{
    date = DateTime.Now.ToShortDateString();
    await nextMiddleware.Invoke();                 // вызываем middleware из app.Run
    Console.WriteLine($"Current date: {date}");  // Current date: 08.12.2021
});

app.Run(async(context) => await context.Response.WriteAsync($"Date: {date}"));

app.Run();

В данном случае мы используем перегрузку метода Use, которая в качестве параметров принимает контекст запроса - объект HttpContext и делегат Func<Task>, который представляет собой ссылку на следующий в конвейере компонент middleware.

Middleware в методе app.Use() реализует простейшую задачу - присваивает переменной date текущую дату в виде строки и затем передает обработку запроса следующим компонентам middleware в конвейере. То есть при вызове Await nextMiddleware.Invoke() обработка запроса перейдет к тому компоненту, который установлен в методе app.Run(). В итоге обработка запроса будет выглядеть следующим образом:

  1. Вызов компонента app.Use()

  2. Установка значения переменной date:

    currentDate = Now.ToShortDateString()
  3. Вызов Await nextMiddleware.Invoke(). Управление переходит следующему компоненту в конвейере - к app.Run.

  4. В middleware из app.Run() отравляет клиенту текущую дату в качестве ответа с помощью метода context.Response.WriteAsync():

    Await context.Response.WriteAsync($"Date: {currentDate}")
    
  5. Метод app.Run закончил свою работу, и управление обработкой возвращается к middleware в методе app.Use. Начинает выполняться та часть кода, которая идет после Await nextMiddleware.Invoke(). В этой части выполняется условное логгирование - на консоль выводится значение переменной date:

    Console.WriteLine($"Current date: {currentDate}")

    После этого обработка запроса завершена

В итоге в веб-браузере мы увидим следующее сообщение:

Обработка запроса методом app.Use в ASP.NET Core и Visual Basic .NET

А в консоли запущенного приложения мы увидим значение переменной date, которое выводится в middleware из метода app.Use:

Создание конвейера middleware с помощью метода app.Use в ASP.NET Core и Visual Basic .NET

Отправка ответа

При использовании метода Use и передаче выполнения следующему делегату следует учитывать, что не рекомендуется вызывать метод nextMiddleware.Invoke после метода Response.WriteAsync(). Компонент middleware должен либо генерировать ответ с помощью Response.WriteAsync, либо вызывать следующий делегат посредством nextMiddleware.Invoke, но не выполнять оба этих действия одновременно. Так как согласно документации последующие изменения объекта Response могут привести к нарушению протокола, например, будет послано больше байт, чем указано в заголовке Content-Length, либо могут привести к нарушению тела ответа, например, футер страницы HTML запишется в CSS-файл.

То есть к примеру следующая обработка запроса не рекомендуется:

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

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

        ' Первый middleware
        app.Use(Async Function(context As HttpContext, nextMiddleware As Func(Of Task)) As Task

                    Await context.Response.WriteAsync("<p>Hello world!</p>")
                    Await nextMiddleware.Invoke()
                End Function)
        ' Второй middleware
        app.Run(Async Function(context As HttpContext) As Task
                    Await Task.Delay(10000) 'можно поставить задержку
                    Await context.Response.WriteAsync("<p>Good bye, World...</p>")
                End Function)

        app.Run()
    End Sub
End Module

Использование делегат RequestDelegate

В примере выше использовалась версия метода Use(), которая использует делегат Func(Of Task). Подобным образом можно использовать и другую версию, где используется делегат RequestDelegate. Единственное - при вызове делегата ( то есть фактически следующего в конвейере компонента) необходимо передавать делегату объект HttpContext:

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

        Dim builder = WebApplication.CreateBuilder(args)
        Dim app = builder.Build()
        Dim currentDate = ""    ' текущая дата

        app.Use(Async Function(context As HttpContext, nextMiddleware As RequestDelegate) As Task

                    currentDate = Now.ToShortDateString()
                    Await nextMiddleware.Invoke(context)
                    Console.WriteLine($"Current date: {currentDate}")  ' Current date: 15.05.2022
                End Function)

        app.Run(Async Function(context As HttpContext) As Task
                    Await context.Response.WriteAsync($"Date: {currentDate}")
                End Function)

        app.Run()
    End Sub
End Module

Терминальный компонент middleware

Middleware в методе Use() необязательно должен вызывать к следующему в конвейере компоненту. Вместо этого он может завершить обработку запроса. В этом случае он может выступать в роли такого же терминального компонента middleware, а и компоненты из метода Run(). Например:

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

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

        app.Use(Async Function(context As HttpContext, nextMiddleware As RequestDelegate) As Task

                    Dim path = context.Request.Path.Value?.ToLower()
                    If path = "/date" Then
                        Await context.Response.WriteAsync($"Date: {Now.ToShortDateString()}")
                    Else
                        Await nextMiddleware.Invoke(context)
                    End If
                End Function)

        app.Run(Async Function(context As HttpContext) As Task
                    Await context.Response.WriteAsync("Hello METANIT.COM")
                End Function)

        app.Run()
    End Sub
End Module

Здесь middleware в app.Use проверяет запрошенный адрес - если он содержит "/date", то клиенту отправляется текущая дата. Иначае обработка запроса передается дальше в app.Run.

Терминальный компонент middleware в app.Use в ASP>NET Core и Visual Basic .NET

Причем в принципе мы можем использовать компонент в app.Use как единственный и соответственно терминальный:

Imports Microsoft.AspNetCore.Builder
Imports Microsoft.AspNetCore.Http

Module Program
    Sub Main(args As String())

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

        app.Use(Async Function(context As HttpContext, nextMiddleware As RequestDelegate) As Task

                    Await context.Response.WriteAsync("Hello Work!")
                End Function)

        app.Run()
    End Sub
End Module

Однако в данном случае для большей производительости лучше использовать app.Run(), если нам надо определить лишь один компонент, который в принципе не передает запрос дальше по конвейеру.

Вынесение компонентов в методы

Также можно вынести все inline-компоненты в отдельные методы:

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

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

        app.Use(AddressOf GetDate)
        app.Run(AddressOf Hello)

        app.Run()
    End Sub

    Async Function GetDate(context As HttpContext, nextMiddleware As RequestDelegate) As Task

        Dim path = context.Request.Path.Value?.ToLower()
        If path = "/date" Then
            Await context.Response.WriteAsync($"Date: {Now.ToShortDateString()}")
        Else
            Await nextMiddleware.Invoke(context)
        End If
    End Function

    Async Function Hello(context As HttpContext) As Task
        Await context.Response.WriteAsync("Hello METANIT.COM")
    End Function
End Module
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850