Добавление заголовков ответов к промежуточному средству ASP.NET Core

Я хочу добавить промежуточное программное обеспечение для обработки в свой ASP.NET Core WebApi, как это

public class ProcessingTimeMiddleware  
{
    private readonly RequestDelegate _next;

    public ProcessingTimeMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        var watch = new Stopwatch();
        watch.Start();

        await _next(context);

        context.Response.Headers.Add("X-Processing-Time-Milliseconds", new[] { watch.ElapsedMilliseconds.ToString() });
    }
}

Но это делает исключение, говорящее

 System.InvalidOperationException: Headers are readonly, reponse has already started.

Как добавить заголовки в ответ?

Ответ 1

Ничего, код здесь

    public async Task Invoke(HttpContext context)
    {
        var watch = new Stopwatch();
        watch.Start();

        //To add Headers AFTER everything you need to do this
        context.Response.OnStarting(state => {
            var httpContext = (HttpContext)state;
            httpContext.Response.Headers.Add("X-Response-Time-Milliseconds", new[] { watch.ElapsedMilliseconds.ToString() });
            return Task.FromResult(0);
        }, context);

        await _next(context);
    }

Ответ 2

Использование перегрузки метода OnStarting:

public async Task Invoke(HttpContext context)
{
    var watch = new Stopwatch();

    context.Response.OnStarting(() =>
    {
        watch.Stop();

        context
              .Response
              .Headers
              .Add("X-Processing-Time-Milliseconds",
                        new[] { watch.ElapsedMilliseconds.ToString() });

        return Task.CompletedTask;
    });

    watch.Start();

    await _next(context); 
}

Ответ 3

В ваших примерах уже отправлены заголовки, когда выполнение достигает context.Response.Headers.Add(...).

Вы можете попробовать:

public async Task Invoke(HttpContext context)
{
    var watch = new Stopwatch();
    context.Response.OnSendingHeaders(x =>
    {
        watch.Stop();
        context.Response.Headers.Add("X-Processing-Time-Milliseconds", new[] { watch.ElapsedMilliseconds.ToString() });
    }, null);

    watch.Start();
    await _next(context);
    watch.Stop();
}

Ответ 4

В качестве альтернативы вы также можете добавить промежуточное программное обеспечение непосредственно в метод Startup.cs Configure.

        app.Use(
            next =>
            {
                return async context =>
                {
                    var stopWatch = new Stopwatch();
                    stopWatch.Start();
                    context.Response.OnStarting(
                        () =>
                        {
                            stopWatch.Stop();
                            context.Response.Headers.Add("X-ResponseTime-Ms", stopWatch.ElapsedMilliseconds.ToString());
                            return Task.CompletedTask;
                        });

                    await next(context);
                };
            });

        app.UseMvc();