Содержание
Обработка запросов в ASP.NET Core построена по принципу конвейера, состоящего из делегатов запроса. Делегат запроса представляет собой некий фрагмент кода, который может выглядеть как обычный анонимный метод или класс C#. Делегаты запроса называются ПО промежуточного слоя. Также можно встретить такие названия как компонент middleware или просто — middleware. Компонент middleware может выполнять работу как до, так и после вызова следующего компонента в конвейере запроса или же не вызывать следующий компонент конвейера — в этом случае компонент middleware будет называться терминальным. В этой части мы рассмотрим основные моменты, касающиеся организации конвейера обработки запросов в приложениях ASP.NET Core Web API.
Конвейер обработки запросов в приложении Web API
Конвейер обработки запросов в приложении ASP.NET Core Web API настраивается с использованием объекта WebApplication перед запуском приложения. В общем случае, конвейер обработки запросов в приложении ASP.NET Core Web API можно представить в виде следующей схемы:

Рассмотрим эту схему.
- Пользователь отправляет запрос серверу Web API, который попадает в конвейер обработки запросов, состоящий из нескольких компонентов Middleware.
- Middleware 1 и Middleware 2 содержат вызов
await next()
, который вызывает следующий middleware в конвейере. По мере прохождения от одного middleware к другому слева на право производится модификация запроса. На этом этапе мы можем, например, проверить наличие определенных заголовков в запросе пользователя. - После прохождения Middleware N модифицированный каким-либо образом запрос попадает в определенный контроллер для чего используется система маршрутизации о которой мы ещё поговорим.
- В контроллере выполняются действия, например, производится запрос к БД для получения данных. После получения данных, запрос уже содержащий какие-то данные ответа вновь попадает в конвейер и начинается его движение в обратном направлении — справа на лево. При этом, каждый компонент middleware также может производить модификацию ответа. Здесь мы можем, опять же, добавить какой-нибудь заголовок, изменить тело ответа и т.д.
- В итоге, Middleware 1 возвращает уже готовый ответ пользователю.
ASP.NET Core уже содержит ряд готовых middleware, которые мы можем встраивать в конвейер запросов (и в подавляющем большинстве случаев мы и будем ими пользоваться). Так, например, в «пустом» приложении ASP.NET Core Web API мы можем встретить такие вызовы:
app.UseHttpsRedirection(); app.UseAuthorization();
Здесь UseHttpsRedirection()
— это метод расширения, который встраивает в конвейер обработки запросов middleware для перенаправления всех запросов на HTTPS, а UseAuthorization()
подключает middleware для поддержки проверки подлинности (аутентификации пользователя).
На данный момент, в ASP.NET Core содержится порядка тридцати готовых к использованию компонентов middleware — для проверки подлинности, поддержки кэширования, статических файлов, обработки исключений, сжатия ответов и т.д. По мере необходимости мы будем задействовать эти компоненты в наших приложениях. Для встраивания этих компонентов в конвейер обработки запроса у интерфейса IApplicationBuilder
определены методы расширения типа UseXXX
.
Пример компонента middleware
Рассмотрим пример использования собственного компонента middleware в приложении ASP.NET Core Web API:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi builder.Services.AddOpenApi(); var app = builder.Build(); var token = "12345"; //строка для проверки var valid_token = "12345"; if (app.Environment.IsDevelopment()) { app.MapOpenApi(); } app.UseHttpsRedirection(); app.UseAuthorization(); //свой middleware app.Use(async (context, next) => { if (token == valid_token) await next(); else await context.Response.WriteAsync("Нет доступа"); }); app.MapControllers(); app.Run();
В этом примере мы используем один из способов встраивания своего middleware в конвейер обработки запросов — метод Use()
.
app.Use(async (context, next) => { if (token == valid_token) await next(); else await context.Response.WriteAsync("Нет доступа"); });
В middleware некая строка token
сравнивается со значение valid_token
. Если строки равны, то вызывается следующий middleware в конвейере обработки запросов, иначе — в теле ответа возвращается строка «Нет доступа
«.
Запустим наше приложение, используя http-файл. После запуска приложения мы увидим, что запрос выполнен успешно:
теперь изменим значение token
, например, так:
var token = "67890";
снова запустим приложение и попробуем получить данные:
Этот пример наглядно демонстрирует схему работы конвейера обработки запросов, показанную выше — запрос от пользователя последовательно обрабатывается каждым middleware в конвейере и запрос либо доходит до выполнения запроса данных, либо возвращается сообщение об отсутствии доступа.
Так как результат работы одного компонента может обрабатываться другим компонентом, то стоит особо отметить, что порядок расположения middleware в конвейере обработки запросов важен для работы приложения.
Жизненный цикл middleware
Ещё один важный момент работы конвейера обработки запросов в ASP.NET Core — компоненты middleware создаются один раз при запуске приложения. Для демонстрации этого момента можно изменить наш компонент следующим образом:
DateTime time = DateTime.Now; //свой middleware app.Use(async (context, next) => { if (token == valid_token) await next(); else await context.Response.WriteAsync($"Попытка доступа {time}"); });
В данном случае мы ожидаем, что при попытке доступа с неверным token
мы будем получать сообщение, содержащее точное время попытки доступа доступа. Запустим приложение и попробуем запросить данные дважды с небольшим интервалом времени:
первая попытка:
вторая попытка:
Как можно видеть по рисункам, время доступа осталось неизменным, хотя запросы шли с задержкой. Это доказывает то, что наш компонент middleware создался один раз и, в дальнейшем, используется в конвейере обработки запросов ASP.NET Core.
Итого
В ASP.NET Core обработка запроса пользователя осуществляется по принципу конвейера. Конвейер обработки запросов состоит из компонентов middleware (ПО промежуточного слоя), которые создаются один раз при создании приложения и выстраиваются в конвейере в определенном порядке от которого зависит работа приложения. В приложениях ASP.NET Core Web API на основе контроллеров собственные middleware используются не так часто, как, например, при использовании minimal API (чаще мы будем подключать в проекте уже готовые компоненты middleware), однако, стоит знать и понимать каким образом middleware могут влиять на работу нашего приложения.