Response buffering entails storing output responses. Response caching enables browsers and other clients to retrieve a server's response swiftly and efficiently in response to subsequent requests. Response caching in ASP.NET Core reduces server burden and enhances the user experience in web applications. This blog will provide a comprehensive explanation of response caching in ASP.NET Core.

What exactly is Response Cache?

Using the response cache, the server can store responses in memory or on disk so that subsequent requests can retrieve them rapidly. Caching mechanisms examine the cache for responses whenever a server request is made. The cache returns the response rather than generating a new one. Using response caching decreases the server's burden and the number of requests made to the server.

Note the HTTP caching directives and how they can be used to control caching behavior.

Response Header Caching
To cache the response, the 'Client and Server' exchange HTTP header information. How HTTP caching directives can be used to control caching behavior. Cache control specifies the manner in which the response can be retained. When the cache-control header is present in the response, it is the responsibility of browsers, clients, and proxy servers to honor it.

Principal Response Caching Headers appear as follows:

  • Cache-Control Pragmatism
  • Vary
  • Header with the Cache-Control directive

The Cache-Control header is the primary response caching header. To add a Cache-Control header in ASP.Net Core, use the Response object in the action method of your controller. So, let's begin with the most prevalent cache-control directives:

  • This cache can either store the response on the device or in a shared location.
  • This Private Cache always stores Client Side Response, but does not purge the cache on the client side.
  • max-age: This cache-control directive indicates how long a response should be stored in the cache.
  • no-cache: This value denotes that the client should not store a copy of the response in its cache.
  • no-store: This cache is not permitted to retain the response.

Pragma Header
The Pragma header can control cache performance for ASP.NET Core. Included in the Pragma preface are server and client instructions. If the response is decorated with Cache-Control, Pragma is omitted.

Change Header
The Vary HTTP response header is included in this method's request message, and the URL is the response's body.

ResponseCache Property

ResponseCache attributes specify header properties for response cache headers in ASP.NET Core web applications. This attribute can be applied at either the controller or endpoint level.

Following are several input parameters for the cache attribute.

Duration
You can set or retrieve the cached duration of the response in seconds. This defines the "max-age" attribute within the "cache-control" header. The max-age header, which is used to specify the cache duration, will be generated based on the duration of this property.

Location
specifies the Location where data from a specific URL will be cached. If the Location is decorated with "ResponseCacheLocation.Client," it functions as a cached response on the client and adds "cache-control" to the private header.

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

Note: If you are able to examine the cached response, follow the links on web pages or use Swagger to execute API endpoints in the browser.

Otherwise, if you attempt to refresh the page or revisit the URI, the browser will always request a new response from the server 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}");
    }
}

C#

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.