将响应标头添加到ASP.NET核心中间件

时间:2016-05-23 15:43:28

标签: c# 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.

如何在回复中添加标题?

5 个答案:

答案 0 :(得分:41)

没关系,代码在这里

    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);
    }

答案 1 :(得分:6)

或者,您也可以直接在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();

答案 2 :(得分:4)

使用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 :(得分:4)

在将任何内容写入响应主体之后无法设置响应标头。一旦将请求传递给下一个中间件并将其写入响应,则中间件无法设置响应标题。

但是,有一种使用Callback方法的解决方案。

Microsoft.AspNetCore.Http.HttpResponse定义OnStarting方法,添加要在响应标头发送到客户端之前调用的委托。您可以将此方法视为回调在写入响应之前将立即调用的方法开始。

public class ResponseTimeMiddleware
    {
        private const string RESPONSE_HEADER_RESPONSE_TIME = "X-Response-Time-ms";

        private readonly RequestDelegate _next;

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

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

            context.Response.OnStarting(() => 
            {
                watch.Stop();
                var responseTimeForCompleteRequest = watch.ElapsedMilliseconds;
                context.Response.Headers[RESPONSE_HEADER_RESPONSE_TIME] =  responseTimeForCompleteRequest.ToString(); 
                return Task.CompletedTask;
            });

            // Call the next delegate/middleware in the pipeline
            return this._next(context);
        }
    }

答案 4 :(得分:2)

在已经发送的示例标头中,当执行到达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();
}