In this tip, you will learn how to add execution time as response header in ASP.NET Core.
Background
The idea is to capture the execution time and add it to the response header. Execution time 1081 milliseconds will be added in response header as x-response-time: 1081ms
. For this purpose, we can use Middleware or Filter, any of them, or both. I have seen a similar thing called response-time for Node.js. Here is the code sample to do things for ASP.NET Core.
Common
This is an interface for the stopwatch, which will be used in the middleware/filter to calculate the execution time ElapsedMilliseconds
:
namespace Cpm.Web.Api.Core
{
public interface IStopwatch
{
long ElapsedMilliseconds { get; }
void Start();
void Stop();
void Reset();
}
}
Middleware
Here, in the middleware, we using the injected stopwatch IMiddlewareResponseTimeStopwatch
where it has been injected as a scope. Before processing or after processing Start()
and Stop()
the stopwatch inside Invoke.
And from the time difference, we will get the execution time.
using System.Diagnostics;
using Cpm.Web.Api.Core;
namespace Cpm.Web.Api.Middlewares
{
public class ResponseTimeMiddleware
{
private readonly RequestDelegate _next;
public ResponseTimeMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context,
IMiddlewareResponseTimeStopwatch watch)
{
watch.Start();
context.Response.OnStarting(state =>
{
watch.Stop();
string value = string.Format("{0}ms", watch.ElapsedMilliseconds);
context.Response.Headers["X-Response-Time"] = value;
return Task.CompletedTask;
}, context);
await _next(context);
}
}
public interface IMiddlewareResponseTimeStopwatch : IStopwatch
{
}
public class MiddlewareResponseTimeStopwatch : Stopwatch,
IMiddlewareResponseTimeStopwatch
{
public MiddlewareResponseTimeStopwatch() : base()
{
}
}
}
Action Filter
The filter is similar to middleware, only Start()
inside OnActionExecuting
and Stop()
inside OnActionExecuted.
IActionResponseTimeStopwatch
stopwatch is also from services.
using Cpm.Web.Api.Core;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Diagnostics;
namespace Cpm.Web.Api.Filters
{
[AttributeUsage(validOn: AttributeTargets.Class | AttributeTargets.Method)]
public class ResponseTimeFilter : Attribute, IActionFilter
{
private IActionResponseTimeStopwatch GetStopwatch(HttpContext context)
{
return context.RequestServices.GetService<IActionResponseTimeStopwatch>();
}
public void OnActionExecuting(ActionExecutingContext context)
{
IStopwatch watch = GetStopwatch(context.HttpContext);
watch.Reset();
watch.Start();
}
public void OnActionExecuted(ActionExecutedContext context)
{
IStopwatch watch = GetStopwatch(context.HttpContext);
watch.Stop();
string value = string.Format("{0}ms", watch.ElapsedMilliseconds);
context.HttpContext.Response.Headers["X-Action-Response-Time"] = value;
}
}
public interface IActionResponseTimeStopwatch : IStopwatch
{
}
public class ActionResponseTimeStopwatch : Stopwatch, IActionResponseTimeStopwatch
{
public ActionResponseTimeStopwatch() : base()
{
}
}
}
Using Middleware/Filter
Let's use the filter and middleware in Startup.cs or in the bootstrap file.
Add Filter
In void ConfigureServices(IServiceCollection services)
, do dependency injection:
services.AddScoped<IActionResponseTimeStopwatch, ActionResponseTimeStopwatch>();
In void ConfigureServices(IServiceCollection services)
, add:
services.AddMvc(options =>
{
options.Filters.Add(new ResponseTimeFilter());
});
This filter will get applied to all actions.
Add Middleware
In void ConfigureServices(IServiceCollection services)
, do dependency injection:
services.AddScoped<IMiddlewareResponseTimeStopwatch, MiddlewareResponseTimeStopwatch>();
In void Configure(IApplicationBuilder app, IWebHostEnvironment env)
, add:
app.UseMiddleware<ResponseTimeMiddleware>();
Testing Middleware/Filter
Curl
curl -X GET "https://localhost:7178/api/Hello" -H "accept: text/plain"
Response Headers
content-type: application/json; charset=utf-8
date: Mon18 Jul 2022 10:25:18 GMT
server: Kestrel
x-action-response-time: 1008ms
x-response-time: 1081ms
x-trace-id: 0c635697-5606-4417-970f-6cdeebbc60ed
x-action-response-time: 1008ms
added from filter and x-response-time: 1081ms
from middleware.
Important
We need to make sure IActionResponseTimeStopwatch
and IMiddlewareResponseTimeStopwatch
injected as scope and no one else is using them except this middleware and filter.
In the middleware, we can skip injection and use Stopwatch
.
public async Task Invoke(HttpContext context)
{
var watch = new Stopwatch();
watch.Start();
}
References
About Code Sample
- Visual Studio 2022 Solution
- ASP.NET Core 6
- This example is also tested in 5, 3.1
History
- 18th July, 2022: Initial version