将响应标头添加到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/找到该规范。