Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET / ASP.NET-Core

Response Time Header in ASP.NET Core

5.00/5 (5 votes)
18 Jul 2022CPOL1 min read 11.2K   91  
Add execution time as a response header in ASP.NET Core
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:

C#
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.

C#
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.

C#
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:

C#
services.AddScoped<IActionResponseTimeStopwatch, ActionResponseTimeStopwatch>();

In void ConfigureServices(IServiceCollection services), add:

C#
/*Filter*/
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:

C#
services.AddScoped<IMiddlewareResponseTimeStopwatch, MiddlewareResponseTimeStopwatch>();

In void Configure(IApplicationBuilder app, IWebHostEnvironment env), add:

C#
/*Middleware*/
app.UseMiddleware<ResponseTimeMiddleware>();

Testing Middleware/Filter

Curl

XML
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.

C#
public async Task Invoke(HttpContext context)
{
    var watch = new Stopwatch();
    watch.Start();
    /*others same*/
}

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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)