What is Response Caching in ASP.NET Core?

Overview 

Response caching means storing response output. Browsers and other clients use response caching to cache a server's response in order to retrieve it quickly and efficiently in response to subsequent requests. In ASP.NET Core, response caching reduces server load and improves user experience in web applications. The purpose of this blog is to provide a detailed explanation of response caching in ASP.NET Core.

What is Response Caching?

Using the response cache, the server can store responses in memory or on disk so that they can be retrieved quickly for subsequent requests. Caching mechanisms check the cache for responses whenever a request is made to the server. Rather than generating a new response, the cache returns the response. Using response caching reduces some server workload and reduces some requests to the server.

Note. HTTP caching directives and how we can control caching behavior using them

Response Caching Headers

The 'Client and Server' exchange HTTP header information to cache the response. HTTP caching directives and how we can control caching behavior using them. Cache control specifies how the response can be cached. It is the responsibility of browsers, clients, and proxy servers to honor the cache-control header when it is present in the response.

Main Response Caching Headers are like below,

  • Cache-Control
  • Pragma
  • Vary
  • Cache-Control Header

The Cache-Control header is the main header type for response caching. To add a Cache-Control header in ASP.Net Core, you can use the Response object in your controller's action method. So, Let's Start with the common cache-control directives:

  1. public: this cache can store the response either on the client side or at a shared location.
  2. private: This Private Cache always stores Client Side Response But does Not Shred the Cache From The Client Side.
  3. max-age: this cache-control header represents a time to hold a response in the cache.
  4. no-cache: this value indicates that the client should not cache the response.
  5. no-store: this cache must not store the response.

Pragma Header

For ASP.NET Core, the Pragma header can control cache performance. Server and client instructions are included in the Pragma header. Pragma is skipped if the response is decorated with Cache-Control.

Vary Header

The Vary http response header is part of the request message from this method, and the URL is the content of the response.

ResponseCache Attribute

ResponseCache attributes define header all properties for response cache headers in the ASP.NET Core web app. This attribute can be applied at the controller level or each endpoint.

Below, you will find a few input parameters for the cache attribute.

Duration

You can set or retrieve the response's cached duration in seconds. This defines "max-age" in the "cache-control" header. The max-age header, which is used to specify the cache duration, will be produced by this property's duration.

Location

sets the Location wherever the data from a particular URL should be cached. If the Location is decorated with "ResponseCacheLocation.Client," it works as a cached response in the client and sets "cache-control" to the private header.

In this example, the Location is decorated with "ResponseCacheLocation.None" operates as "cache-control," and the "Pragma" header is set to "no-cache".

Note. If you can check the cache response, then click the links on web pages or use Swagger to run API endpoints in the browser. 

Otherwise, if you try to refresh the page or revisit the URI page, the browser will always request a new response from the server side, regardless of the response cache settings.

Public

​public class HomeController: Controller
{
    [HttpGet]
    [ResponseCache(Duration = 180, Location = ResponseCacheLocation.Any)]
    public IActionResult getCache()
    {
        return Ok($"Responses are generated on {DateTime.Now}");
    } 
}

This Duration property will generate the max-age header, which we use to define the duration of the cache for 3 minutes (180 seconds). The Location property will define the Location within the cache-control header.

So, the API endpoint and verify these response headers:

cache-control: public,max-age=180

​The status code indicates that the response comes from the disk cache:

Status Code: 200

Private

We just need to change the Location property in ResponseCacheLocation.Client to private:

​public class HomeController: Controller
{
    [HttpGet]
    [ResponseCache(Duration = 180, Location = ResponseCacheLocation.Client)]
    public IActionResult getCache()
    {
        return Ok($"Responses are generated on {DateTime.Now}");
    } 
}

This changes the value of the cache control header to private, which means that only the client can cache the response:

cache-control: private,max-age=180

No-Cache

Now let us update the Location parameter to ResponseCacheLocation.  None:

public class HomeController: Controller
{
    [HttpGet]
    [ResponseCache(Duration = 180, Location = ResponseCacheLocation.None)]
    public IActionResult getCache()
    {
        return Ok($"Responses are generated on {DateTime.Now}");
    } 
}

Because the cache-control and pragma headers are set to no-cache, the client is unable to use a cached response without first verifying it with the server:

cache-control: no-cache,max-age=180

pragma: no-cache

The server generates a new response each time, and the browser does not use the cached response.

NoStore 

Gets or sets the value to determine whether to store the data. If NoStore is decorated with the value "true", the "cache-control" header is set to "no-store". It ignores the "Location" and the parameter has ignored the values; otherwise values "None".

​public class HomeController: Controller
{
    [HttpGet]
    [ResponseCache(Duration = 180, Location = ResponseCacheLocation.Any,NoStore =True)]
    public IActionResult getCache()
    {
        return Ok($"Responses are generated on {DateTime.Now}");
    } 
}

This sets the response header cache control to no-store. This means that the client should not cache the response:

cache-control: no-store

VaryByHeader

Sets or gets the "Vary" response header value. ResponseCache's VaryByHeader property allows us to set the vary header:

Now that User-Agent is the value for the VaryByHeader property, the cached response will be used as long as the request originates from the same client device. Once the User-Agent value on the client device changes, a new response will be fetched from the server. Let's verify this.

​public class HomeController: Controller
{
    [HttpGet]
    [ResponseCache(Duration = 180, Location = ResponseCacheLocation.Any,VaryByHeader="User-Agent")]
    public IActionResult getCache()
    {
        return Ok($"Responses are generated on {DateTime.Now}");
    } 
}

In the response headers, check for the Vary header:

vary: User-Agent

The application is run in desktop mode, then you can see the response header "Vary" contains the value "User-Agent" (see below).

  • user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36
  • user-agent: Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1

VaryByQueryKeys Property

The VaryByQueryKeys property can be used to trigger the server to deliver a fresh response every time the response will change. There is a change in query string parameters. 

​public class HomeController: Controller
{
    [HttpGet]
    [ResponseCache(Duration = 180, Location = ResponseCacheLocation.Any, VaryByQueryKeys = new string[] { "Id " })]
    public IActionResult getCache()
    {
        return Ok($"Responses are generated on {DateTime.Now}");
    } 
}

For example, When the Id value change and then URI also change, and  we want to generate a new response:

/api/Home?Id=1

/api/Home?Id=2

Cache Profile

In this project, you can use Response Cache attributes, as most action methods have the same input parameters. With ASP.Net Core, all parameter options are related in a Program class and with it's name and which can be used in the Response Cache attribute to remove duplicate parameter settings.

Cache3 is a new cache profile that has a time duration of 3 minutes and a location of the public.

builder.Services.AddControllers (option =>
{
   option.Cache Profiles.Add("Cache3",
      new CacheProfile()
      {
          Duration= 180,
          Location = ResponseCacheLocation.Any
      });
});
public class HomeController: Controller
{ 
    [HttpGet]
    [ResponseCache (Cache ProfileName ="Cache3")]
     public IActionResult getCache()
     {
          return Ok ($"Responses are generated on (DateTime.Now}");
     }
}

The defined cache-control response (see below):

cache-control: public,max-age=180

Caching Middleware 

Middleware can be written to add a response cache, but this implementation adds a response cache to every page.

Program.cs File

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(); 
builder.Services.AddResponseCaching();

var app = builder.Build();

app.MapControllers();
app.UseResponseCaching () ;
app.Run();
​public class HomeController: Controller
{
    [HttpGet]
    [ResponseCache(Duration = 180, Location = ResponseCacheLocation.Any, VaryByQueryKeys = new string[] { "Id" })]
    public IActionResult getCache(int Id)
    {
        return Ok($"Responses are generated on Id:{Id} at {DateTime.Now}");
    } 
}

First, add the response caching middleware using the AddResponseCaching() method, then configure the app to use it with UseResponseCaching().

That's it. Response caching middleware has been enabled, so VaryByQueryKeys should now work.

Let us start the application and navigate to the /Home?id=1 endpoint:

The response was generated for Id:1 at 23-05-2022 05:52:50

Changing the query string resulted in a new response from the server.

Let's change the query string to /Home?id=2:

The response was generated for Id:2 at 23-05-2022 05:53:45

Conclusion

ASP.NET Core's response caching feature allows web applications to scale and perform better. It is possible to speed up and improve page loading efficiency by caching responses at the server or client level.

ASP.NET Core lets you configure caching middleware to cache responses based on URL path, query string parameters, and HTTP headers. Additionally, you can customize the caching behavior using options such as cache expiration times, cache location, and cache key prefixes.

You can increase user satisfaction and reduce hosting costs by using response caching in ASP.NET Core. If you're building a high-traffic website or web application, response caching should be considered a key optimization strategy.


Similar Articles