Что такое middleware в ASP.NET core
Middleware в ASP.NET Core — это компонент, который участвует в обработке входящего HTTP-запроса и/или формировании ответа. Middleware компоненты выстраиваются в конвейер (pipeline), и каждый из них может:
-
обрабатывать запрос до передачи следующему компоненту;
-
решать, передавать ли запрос дальше;
-
изменять ответ после того, как его сформируют другие middleware;
-
завершать обработку запроса полностью.
Это позволяет реализовать перехват, модификацию, фильтрацию и расширение обработки запросов и ответов.
🔷 Основная архитектура middleware
Запросы обрабатываются в ASP.NET Core через последовательную цепочку вызовов. Каждый middleware получает:
-
HttpContext, который содержит всю информацию о запросе и ответе;
-
ссылку на следующий middleware в цепочке.
Простейшая визуализация:
\[Client\] → \[Middleware 1\] → \[Middleware 2\] → \[Middleware N\] → \[Endpoint\]
↑
Возврат ответа
🔶 Пример базового middleware
public class SimpleMiddleware
{
private readonly RequestDelegate \_next;
public SimpleMiddleware(RequestDelegate next)
{
\_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// Логика до следующего middleware
Console.WriteLine("До");
await \_next(context); // Вызов следующего middleware
// Логика после следующего middleware
Console.WriteLine("После");
}
}
Регистрируется в Startup.cs или Program.cs:
app.UseMiddleware<SimpleMiddleware>();
🔷 Виды встроенных middleware
ASP.NET Core поставляется с множеством встроенных middleware, например:
Middleware | Назначение |
---|---|
UseRouting | Определяет маршрут запроса и направляет к соответствующему endpoint. |
--- | --- |
UseAuthentication | Проверяет, аутентифицирован ли пользователь. |
--- | --- |
UseAuthorization | Проверяет права пользователя на доступ. |
--- | --- |
UseStaticFiles | Отдаёт статические файлы (css, js, изображения и т.п.). |
--- | --- |
UseExceptionHandler | Глобальный перехват исключений. |
--- | --- |
UseCors | Разрешение или ограничение кросс-доменных запросов. |
--- | --- |
UseSession | Управление состоянием сессии. |
--- | --- |
UseHttpsRedirection | Перенаправляет все HTTP-запросы на HTTPS. |
--- | --- |
🔶 Порядок важен
Последовательность регистрации middleware влияет на поведение приложения. Например, если вы зарегистрируете UseAuthorization() до UseAuthentication(), то система не будет знать, кто пользователь — и не сможет правильно авторизовать.
Пример правильного порядка:
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
🔷 Создание inline middleware через Use
Можно добавлять middleware прямо через делегаты:
app.Use(async (context, next) =>
{
Console.WriteLine("Перед");
await next(); // вызов следующего middleware
Console.WriteLine("После");
});
🔷 Разница между Use, Run, Map
-
Use — добавляет middleware, может передавать управление дальше (await next()).
-
Run — завершающий middleware, не вызывает next(), поэтому завершает цепочку.
-
Map — ветвление по пути.
app.Map("/admin", adminApp =>
{
adminApp.Run(async context =>
{
await context.Response.WriteAsync("Admin Area");
});
});
🔷 HttpContext в middleware
Через HttpContext middleware может:
-
читать заголовки запроса: context.Request.Headers
-
писать в тело ответа: context.Response.WriteAsync()
-
устанавливать статус: context.Response.StatusCode = 404;
-
устанавливать cookie: context.Response.Cookies.Append()
-
управлять user principal: context.User
🔷 Примеры применения middleware
🔹 Логгирование запросов
app.Use(async (context, next) =>
{
var path = context.Request.Path;
Console.WriteLine($"Запрос: {path}");
await next();
Console.WriteLine($"Ответ: {context.Response.StatusCode}");
});
🔹 Проверка API-ключа
app.Use(async (context, next) =>
{
if (!context.Request.Headers.ContainsKey("X-Api-Key"))
{
context.Response.StatusCode = 401;
await context.Response.WriteAsync("API Key отсутствует");
return;
}
await next();
});
🔹 Обработка исключений
app.Use(async (context, next) =>
{
try
{
await next();
}
catch (Exception ex)
{
context.Response.StatusCode = 500;
await context.Response.WriteAsync("Произошла ошибка: " + ex.Message);
}
});
🔷 Middleware vs Filters
Middleware | Filters (в ASP.NET Core MVC) | |
---|---|---|
Уровень | Глобальный (весь pipeline) | Только внутри MVC/Web API |
--- | --- | --- |
Контекст | HttpContext | ActionContext, ModelState и т.д. |
--- | --- | --- |
Область | До маршрутизации и после | Внутри действия контроллера |
--- | --- | --- |
Назначение | Общие вещи: логгирование, CORS | Валидация, авторизация, логика в контроллерах |
--- | --- | --- |
🔷 Жизненный цикл middleware
-
ASP.NET Core запускает конвейер запроса.
-
Первый middleware в цепочке:
-
выполняет свою логику;
-
решает, передавать ли дальше.
-
-
Если вызван await next(), то управление переходит к следующему middleware.
-
После прохождения всех middleware управление возвращается обратно по цепочке.
🔷 Тестирование middleware
Вы можете протестировать middleware с помощью встроенных средств тестирования:
\[Test\]
public async Task Middleware_SetsStatusCode()
{
var context = new DefaultHttpContext();
var middleware = new MyCustomMiddleware((ctx) => Task.CompletedTask);
await middleware.InvokeAsync(context);
Assert.AreEqual(403, context.Response.StatusCode);
}
🔷 Взаимодействие с DI
В Startup.cs вы можете инжектить сервисы в middleware:
public class AuthMiddleware
{
private readonly RequestDelegate \_next;
private readonly IAuthService \_auth;
public AuthMiddleware(RequestDelegate next, IAuthService auth)
{
\_next = next;
\_auth = auth;
}
public async Task InvokeAsync(HttpContext context)
{
if (!\_auth.IsAuthorized(context))
{
context.Response.StatusCode = 401;
return;
}
await \_next(context);
}
}
🔷 Использование условий
Middleware можно подключать по условию:
app.UseWhen(context => context.Request.Path.StartsWithSegments("/api"), appBuilder =>
{
appBuilder.UseMiddleware<ApiKeyMiddleware>();
});
Таким образом, middleware в ASP.NET Core позволяет пошагово обрабатывать HTTP-запросы и управлять логикой обработки на всех этапах — от маршрутизации и безопасности до логгирования, ошибок, перехвата данных и формирования ответа.