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

我想像这样在我的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() }); } } 

但这样做会引发exception

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

如何在响应中添加标头?

没关系,代码在这里

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

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

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

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

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

但是,有一种使用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); } } 

在相关的说明中,没有回答你的问题,现在有一个Server-Timing规范,一个标准的标题提供持续时间,以及其他指标。 这应该允许你使用

 Server-Timing: processingTime;dur=12ms 

您可以在https://www.w3.org/TR/server-timing/找到该规范。