Приходилось ли вам писать кастомные middleware в ASP.NET Core? Приведите пример.

«Приходилось ли вам писать кастомные middleware в ASP.NET Core? Приведите пример.» — вопрос из категории ASP.NET Core, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Да, создание кастомного middleware — стандартная практика для сквозной функциональности (cross-cutting concerns). Например, для логирования, обработки исключений, кастомной аутентификации или добавления специфичных заголовков.

Пример: Middleware для логирования времени выполнения запроса и добавления корреляционного ID.

public class RequestLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<RequestLoggingMiddleware> _logger;

    public RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        // Генерация уникального ID для отслеживания цепочки запроса.
        var correlationId = Guid.NewGuid().ToString();
        context.Items["CorrelationId"] = correlationId;

        var stopwatch = Stopwatch.StartNew();

        // Логируем начало обработки.
        _logger.LogInformation(
            "Starting request {Method} {Path} with CorrelationId: {CorrelationId}",
            context.Request.Method,
            context.Request.Path,
            correlationId
        );

        try
        {
            // Передаём управление следующему компоненту в конвейере.
            await _next(context);
        }
        finally
        {
            stopwatch.Stop();
            // Логируем завершение обработки и время выполнения.
            _logger.LogInformation(
                "Completed request {Method} {Path} with status {StatusCode} in {ElapsedMs}ms. CorrelationId: {CorrelationId}",
                context.Request.Method,
                context.Request.Path,
                context.Response.StatusCode,
                stopwatch.ElapsedMilliseconds,
                correlationId
            );
        }
    }
}

// Метод расширения для удобной регистрации.
public static class RequestLoggingMiddlewareExtensions
{
    public static IApplicationBuilder UseRequestLogging(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<RequestLoggingMiddleware>();
    }
}

Регистрация в конвейере (Program.cs):

var app = builder.Build();

// Порядок важен! Логирование должно быть одним из первых.
app.UseRequestLogging();
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();

app.Run();

Зачем это нужно? Такой middleware централизует логирование для всех запросов, добавляет полезный контекст (CorrelationId) и помогает в диагностике проблем с производительностью, не загрязняя код контроллеров.