Click here to Skip to main content
15,884,298 members
Articles / Web Development / ASP.NET / ASP.NET Core
Tip/Trick

Response Time Header in ASP.NET Core

Rate me:
Please Sign up or sign in to vote.
5.00/5 (5 votes)
18 Jul 2022CPOL1 min read 9.8K   86   11   7
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)


Written By
Bangladesh Bangladesh
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionGood post Pin
NguyenHuuAnhTuan6-Feb-23 4:35
NguyenHuuAnhTuan6-Feb-23 4:35 
GeneralMy vote of 5 Pin
Mou_kol2-Aug-22 7:55
Mou_kol2-Aug-22 7:55 
GeneralRe: My vote of 5 Pin
DiponRoy2-Aug-22 20:56
DiponRoy2-Aug-22 20:56 
QuestionGood but room for improvement Pin
Mobster_20-Jul-22 17:58
Mobster_20-Jul-22 17:58 
AnswerRe: Good but room for improvement Pin
DiponRoy21-Jul-22 5:13
DiponRoy21-Jul-22 5:13 
Thank you, I will keep this in mind.
QuestionVery nice Pin
Marc Clifton19-Jul-22 1:25
mvaMarc Clifton19-Jul-22 1:25 
AnswerRe: Very nice Pin
DiponRoy19-Jul-22 1:38
DiponRoy19-Jul-22 1:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.