Приведи пример middleware для вычисления времени выполнения запроса в ASP.NET Core

Ответ

Middleware в ASP.NET Core идеально подходит для перехвата и измерения времени выполнения запросов. Вот практический пример, который добавляет заголовок с длительностью в ответ.

Реализация middleware:

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

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

    public async Task InvokeAsync(HttpContext context)
    {
        var stopwatch = Stopwatch.StartNew();

        // Вызов следующего компонента в конвейере
        await _next(context);

        stopwatch.Stop();

        // Добавляем время в заголовок ответа
        context.Response.Headers.Append("X-Request-Duration-ms", stopwatch.ElapsedMilliseconds.ToString());

        // Логируем для анализа
        _logger.LogDebug("Request to {Path} completed in {ElapsedMs} ms", 
            context.Request.Path, stopwatch.ElapsedMilliseconds);
    }
}

Регистрация в Program.cs:

var builder = WebApplication.CreateBuilder(args);
// ... конфигурация сервисов

var app = builder.Build();

// Регистрируем middleware на раннем этапе конвейера,
// чтобы замерить всё время обработки.
app.UseMiddleware<RequestTimingMiddleware>();

// ... остальная конфигурация конвейера (аутентификация, авторизация, контроллеры)
app.Run();

Ключевые моменты и best practices:

  1. Используйте Stopwatch, а не DateTime, для высокой точности измерений.
  2. Позиция в конвейере критична. Размещение middleware до других компонентов (UseRouting, UseAuthentication) позволяет измерить полное время, включая их работу.
  3. Добавление в заголовки (X-Request-Duration-ms) удобно для клиентов и систем мониторинга (например, для анализа в браузере или через прокси).
  4. Логирование (через ILogger) необходимо для сбора метрик на стороне сервера и последующего анализа в таких системах, как Seq, Elasticsearch или Application Insights.
  5. Для продакшена рассмотрите использование специализированных библиотек (например, AppMetrics или OpenTelemetry), которые предоставляют более богатые возможности для сбора и визуализации метрик производительности.

Ответ 18+ 🔞

Смотри, вот тебе реально полезная штука, если хочешь понять, сколько твой API на самом деле тормозит. Middleware в ASP.NET Core — это как раз то самое место, где можно влезть в каждый запрос и засечь время, пока он там по конвейеру катается.

Вот смотри, как это делается, без всякой академической хуйни:

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

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

    public async Task InvokeAsync(HttpContext context)
    {
        var stopwatch = Stopwatch.StartNew();

        // Пропускаем запрос дальше по трубе, пусть там с ним делают что хотят
        await _next(context);

        stopwatch.Stop();

        // Тыкаем время прямо в заголовок ответа, чтобы клиент тоже видел, сколько его мурыжили
        context.Response.Headers.Append("X-Request-Duration-ms", stopwatch.ElapsedMilliseconds.ToString());

        // А себе в логи пишем, для потомков
        _logger.LogDebug("Request to {Path} completed in {ElapsedMs} ms", 
            context.Request.Path, stopwatch.ElapsedMilliseconds);
    }
}

А вот куда эту прелесть впихнуть в Program.cs:

var builder = WebApplication.CreateBuilder(args);
// ... тут обычно всякие сервисы настраивают

var app = builder.Build();

// Ставим наш таймер ПЕРВЫМ в конвейере, блядь! Чтобы засечь ВСЁ, включая время, 
// которое запрос тратит на маршрутизацию и прочую бюрократию.
app.UseMiddleware<RequestTimingMiddleware>();

// ... а уже потом всё остальное: авторизацию, контроллеры и прочее
app.Run();

Теперь главное, что надо запомнить, а то накосячишь:

  1. Stopwatch — твой друг. Никаких DateTime.Now вычитаний, это неточно, как прогноз погоды. Только Stopwatch.
  2. Место в конвейере — это всё. Поставишь после UseRouting — не замеришь время маршрутизации. Поставишь в самом начале — получишь полную картину, включая все следующие middleware. Думай, что тебе нужно.
  3. Заголовок в ответе — офигенная фича. Фронтендеры или тот, кто дергает твой API, сразу увидят цифру. В браузере в DevTools посмотреть — красота.
  4. Логи — это сила. Без них ты как слепой. Потом в Elasticsearch или Grafana заглянешь и сразу видно, какой эндпоинт жрёт время, как не в себя.
  5. Для больших проектов — бери готовое. Если у тебя продакшен-система, где метрики — это жизнь, лучше посмотри в сторону AppMetrics или OpenTelemetry. Там и графики, и алерты, и всё, что душе угодно. Но для начала, чтобы просто понять механизм, этого middleware выше крыши.