Что такое middleware в ASP.NET и как работает pipeline?
Что проверяют
Понимаете ли вы, что конвейер это цепочка делегатов, где каждый решает, звать ли следующий, и почему порядок регистрации важнее самих компонентов. Следом обычно просят написать свой middleware или объяснить, почему не срабатывает авторизация.
Как отвечать
Middleware в ASP.NET Core это компонент, который получает HttpContext и ссылку на следующий RequestDelegate. Код до await next(context) выполняется на пути запроса, код после него на пути ответа. Конвейер похож на матрёшку: первое звено первым видит запрос и последним видит ответ.
Способы собрать конвейер:
app.Use(...): обычное звено, передаёт запрос дальше или отвечает само и прерывает цепочку;app.Run(...): терминальное звено,nextу него нет;app.Map(...)иapp.MapWhen(...): ответвление по пути или условию, в основной конвейер оно не возвращается, в отличие отUseWhen;- класс-middleware: конструктор с
RequestDelegate nextи методInvokeAsync(HttpContext), подключается черезapp.UseMiddleware<T>()и создаётся один раз на всё приложение. АльтернативаIMiddlewareберётся из DI на каждый запрос.
Типичный порядок: UseExceptionHandler, UseHttpsRedirection, UseStaticFiles, UseRouting, UseCors, UseAuthentication, UseAuthorization, затем эндпоинты. С .NET 6 WebApplication сам добавляет UseRouting и UseEndpoints, поэтому UseEndpoints из старых статей заменяют на app.MapControllers().
Стоит отличить middleware от MVC-фильтров: фильтры работают уже после выбора эндпоинта и видят контекст действия.
Где спотыкаются
Путают порядок. UseAuthorization раньше UseAuthentication не знает пользователя, а UseExceptionHandler в конце конвейера не поймает ошибки звеньев, зарегистрированных до него.
Внедряют scoped-сервис, например DbContext, в конструктор класса-middleware. Класс живёт как singleton, поэтому получается ошибка разрешения или захваченная зависимость. Scoped-сервисы принимают параметрами InvokeAsync.
Меняют заголовки после await next(), когда ответ уже уходит клиенту, и получают InvalidOperationException. Проверять нужно Response.HasStarted или вешать код на Response.OnStarting.