Что такое 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

  1. ASP.NET Core запускает конвейер запроса.

  2. Первый middleware в цепочке:

    • выполняет свою логику;

    • решает, передавать ли дальше.

  3. Если вызван await next(), то управление переходит к следующему middleware.

  4. После прохождения всех 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-запросы и управлять логикой обработки на всех этапах — от маршрутизации и безопасности до логгирования, ошибок, перехвата данных и формирования ответа.