Je souhaite ajouter un middleware de temps de traitement à mon ASP.NET Core WebApi comme ceci
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() });
}
}
Mais faire cela jette une exception disant
System.InvalidOperationException: Headers are readonly, reponse has already started.
Comment puis-je ajouter des en-têtes à la réponse?
Qu'à cela ne tienne, le code est là
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);
}
Vous pouvez également ajouter un middleware directement dans la méthode de configuration Startup.cs.
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();
En utilisant une surcharge de la méthode 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);
}
Les en-têtes de réponse ne peuvent pas être définis après quoi que ce soit ait été écrit dans le corps de la réponse .
Cependant, une solution est disponible en utilisant une méthode de rappel.
Microsoft.AspNetCore.Http.HttpResponse
définit la méthode OnStarting
, qui ajoute un délégué à appeler juste avant que les en-têtes de réponse soient envoyés au client. Vous pouvez considérer cette méthode comme une méthode de rappel qui sera appelée juste avant que l'écriture dans la réponse ne commence.
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);
}
}
Dans votre exemple d'en-têtes déjà envoyés, lorsque l'exécution atteint l'instruction context.Response.Headers.Add (...).
Tu peux essayer:
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();
}
Sur une note connexe, sans répondre à votre question en tant que telle, il existe maintenant une spécification Server-Timing
, un en-tête standard fournissant des durées, entre autres métriques. Cela devrait vous permettre d'utiliser
Server-Timing: processingTime;dur=12ms
Vous pouvez trouver la spécification sur https://www.w3.org/TR/server-timing/