Response Caching in ASP.NET Core

Response Caching in ASP.NET Core

In this article, I will discuss Response Caching (ResponseCacheAttribute) in ASP.NET Core Application with Examples. Please read our previous article discussing Custom Result Filter in ASP.NET Core MVC Application. At the end of this article, you will understand the following pointers:

  1. What is Caching in ASP.NET Core?
  2. Types of Caching in ASP.NET Core
  3. What is Response Caching in ASP.NET Core?
  4. How Response Caching Works in ASP.NET Core?
  5. ResponseCacheAttribute in ASP.NET Core
  6. Response Caching Examples: Basic, No Caching, Caching with VarByHeader, Caching by Location, Caching By VarByQueryKeys.
  7. Custom Cache Profiles in ASP.NET Core.
  8. When to use Response Caching in ASP.NET Core?
What is Caching in ASP.NET Core?

Caching in ASP.NET Core is a technique used to store frequently accessed data in a temporary storage area, which can be quickly retrieved. This improves the performance and scalability of an application by reducing the need to fetch the same data repeatedly from the database or other storage mediums.

Types of Caching in ASP.NET Core: 

ASP.NET Core Supports Several Types of Caching:

  • In-Memory Caching: This is the simplest form of caching, suitable for a single server. This is used to store data in the Memory of the Web Server. It’s fast and suitable for data that doesn’t consume too much memory and doesn’t need to persist beyond the lifetime of the web server process. It is suitable for storing small amounts of data that do not require persistence across server restarts. The IMemoryCache interface is used for in-memory caching and provides methods to set, retrieve, and manage cached items. 
  • Distributed Caching: This is ideal for applications running in multi-server or cloud environments where data needs to be shared across multiple servers. It involves storing data in an external system such as Redis, SQL Server, NCache, etc. It is more complex than in-memory caching but necessary for large-scale applications to ensure consistency across sessions and requests. The IDistributedCache interface in ASP.NET Core provides methods for using distributed cache systems.
  • Response Caching: This is used to cache the responses of HTTP requests. Response caching reduces the number of requests a client or proxy makes to a web server and reduces the amount of work the web server performs to generate a response. Response caching is configured in ASP.NET Core using the ResponseCachingMiddleware. The ResponseCacheAttribute defines cache parameters for an action method, and the caching ResponseCachingMiddleware middleware can handle cache headers for requests and store responses accordingly.

Note: In this article, we will discuss Response Caching. The rest of all is going to be discussed in our upcoming articles.

What is Response Caching in ASP.NET Core?

Response Caching in ASP.NET Core refers to the process of storing the output of a request-response cycle so that future requests for the same resource can be served from the cache instead of regenerating the response from scratch. This technique can significantly improve the performance of a web application, especially for resources that are expensive to generate and don’t change often.

When a web application receives a request, it often performs several operations like database queries, complex calculations, or template rendering to generate the response. These operations can be resource-intensive and time-consuming. If the generated response is the same for each request, it makes sense to store that response after it is generated and then serve the stored response for future requests without repeating all the processing. This is called Response Caching.

How Response Caching Works in ASP.NET Core?

Response Caching in ASP.NET Core is a way to store copies of the responses for HTTP requests to reduce the need for repeated processing and to speed up the delivery of content to clients. It can significantly improve the performance and scalability of a web application. Please look at the following diagram to understand How Response Caching in ASP.NET Core Application.

How Response Caching Works in ASP.NET Core?

Here’s how response caching works in a web context, particularly in ASP.NET Core Web Application:

Initial Request:
  • A client makes a request to the server for a specific resource.
  • The server processes the request, generates the response, and returns the response to the client who initially made the request.
  • Along with the response, the server includes HTTP headers such as Cache-Control, Expires, ETag, and Last-Modified to indicate that the response can be cached and to specify the caching behavior.
Subsequent Requests:
  • When the client (or any intermediate proxy) needs the same resource again, it first checks the cache.
  • Suppose a cached response is available and still fresh (i.e., within the period specified by the Cache-Control header). In that case, the client serves it directly from the cache instead of requesting it from the server again.
Cache Headers:
  • Cache-Control: It specifies how and for how long the response should be cached.
  • Expires: It sets an absolute expiration date for the cached response.
  • ETag: It is used to validate cached responses. A unique identifier for a version of the resource. It helps in conditional requests using If-None-Match.
  • Last-Modified: It is also used to validate cached responses. The timestamp of when the resource was last modified. It is used in conditional requests with If-Modified-Since.

Note: When a client makes a request with conditional headers (If-Modified-Since, If-None-Match), the server can either return the cached content (if it’s still valid) or generate a new response.

What is Proxy Cache in ASP.NET Core?

In the context of ASP.NET Core, proxy caches refer to a caching mechanism where a caching server (the proxy) is placed between the client (usually a web browser) and the server hosting the ASP.NET Core application. This proxy server caches responses from the web server, which can then be served quickly to subsequent requests for the same resource. This can significantly improve the performance of web applications by reducing the load on the web server and decreasing response times for end-users.

Server-Side:
  • You can use the [ResponseCache] attribute in ASP.NET Core to specify caching behavior for controller actions. This attribute allows you to set various parameters like duration, location (client, server, or both), and whether the cache should consider query string values.
  • ASP.NET Core includes UseResponseCaching middleware for caching responses on the server. This UseResponseCaching middleware can be configured in the Startup.cs file or Program.cs file of your application.
Client-Side:
  • Browsers respect the Cache Headers and store the responses in their cache based on their directives, i.e., no-cache, no-store, public, private, etc.
  • Proxy Caches can do the same, helping reduce the load on the server and speeding up response times for users behind the proxy.
Cache Validation:
  • Sometimes, clients might send conditional requests to check if the stored response is still valid (using ETag or Last-Modified).
  • The server can respond with 304 Not Modified if the content hasn’t changed, indicating that the cache can be reused.

So, Response Caching is a way to make web applications faster and more efficient by reusing previously generated responses for certain requests, as long as the content hasn’t changed. 

ResponseCacheAttribute in ASP.NET Core

The ResponseCacheAttribute in ASP.NET Core allows developers to control how responses from Web Applications are cached. This attribute can be applied to controller actions or entire controllers, and it specifies the parameters necessary for setting appropriate HTTP headers for caching responses. The ResponseCache Attribute itself does not cache content. Instead, it sets the appropriate HTTP headers that control the caching behavior. 

Purpose: The ResponseCacheAttribute is used to configure and apply cache-related settings to the responses of controller actions. It configures the Cache-Control, Pragma, and Expires HTTP headers in the response.

Usage: You can apply the ResponseCacheAttribute to an entire controller or to specific actions within a controller. When applied, it affects how client and intermediate proxies cache the responses.

ResponseCacheAttribute Properties:

The ResponseCache Attribute has the following properties:

  • Duration(int): Specifies the time, in seconds, for which the response should be cached. This sets the max-age directive of the Cache-Control header.
  • Location (ResponseCacheLocation): Determines the location where the response can be cached. Options include Any, Client, and None. This setting modifies the Cache-Control header.
  • NoStore (bool): When set to true, it indicates that the response should not be cached (Cache-Control: no-store). This takes precedence over other properties.
  • VaryByHeader (string): Specifies a header name to vary the cached response by (e.g., User-Agent). 
  • VaryByQueryKeys(string): Allows varying the response cache by the query string parameters. It’s useful for caching different responses for different query string values.
  • CacheProfileName(string): Refers to a cache profile defined in the application’s configuration, allowing for centralized cache policy settings.
ResponseCacheAttribute Examples in ASP.NET Core Web API

We must follow the steps below to work with Response Caching in ASP.NET Core Web API:

  • Setup Middleware: To enable Response Caching, we must add the AddResponseCaching services and UseResponseCaching middleware components to the request processing pipeline.
  • Configure Actions: In your controllers, we must use the [ResponseCache] attribute to customize the caching behavior for specific actions. This attribute allows us to set various caching-related properties such as duration, location (client, server, or both), and whether to vary by header or query string.
  • Client-Side and Server-Side Caching: The middleware supports both client-side and server-side caching. Client-side caching is controlled by headers that tell the client’s browser to cache responses. Server-side caching stores responses on the server and reuses them for subsequent requests.

Let us see some examples to understand how to use the ResponseCacheAttribute in an ASP.NET Core application. Let us first create a new ASP.NET Core Web API application.

Configure the Response Caching Middleware

Before using the ResponseCacheAttribute, ensure that the response caching middleware is added to the request pipeline in the Program.cs class file. We need to add the AddResponseCaching services and UseResponseCaching middleware components to the request processing pipeline. So, modify the Program.cs class file as follows:

namespace FiltersDemo
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Add services to the container.

            builder.Services.AddControllers();
            builder.Services.AddResponseCaching();
            // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
            builder.Services.AddEndpointsApiExplorer();
            builder.Services.AddSwaggerGen();

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (app.Environment.IsDevelopment())
            {
                app.UseSwagger();
                app.UseSwaggerUI();
            }

            app.UseHttpsRedirection();
            app.UseResponseCaching();
            app.UseAuthorization();

            app.MapControllers();

            app.Run();
        }
    }
}
Example to Understand Basic Response Caching

Here, the Index action method of the Home Controller is cached for 60 seconds. This means once a response is generated, it will be reused for subsequent requests for the next 60 seconds.

using Microsoft.AspNetCore.Mvc;
namespace FiltersDemo.Controllers
{
    [Route("api/{controller}/{action}")]
    [ApiController]
    public class HomeController : ControllerBase
    {
        [HttpGet]
        [ResponseCache(Duration = 60)]
        public string Index()
        {
            return $"Response Generated at: {DateTime.Now}";
        }
    }
}

Now, run the application and visit the URL /api/home/index, and you will see the following output. Within 60 seconds, if you visit the same endpoint, you will see the same output. Further, if you check the Response headers, you will see the Cache-Control header whose value is set to public,max-age=60, as shown in the image below.

Response Caching in ASP.NET Core Application with Examples

Example: No Caching

In the following example, we have set the NoStore property of the ResponseCache Attribute to true, which tells the browser not to cache the response.

using Microsoft.AspNetCore.Mvc;
namespace FiltersDemo.Controllers
{
    [Route("api/{controller}/{action}")]
    [ApiController]
    public class HomeController : ControllerBase
    {
        [HttpGet]
        [ResponseCache(Duration = 60, NoStore =true)]
        public string Index()
        {
            return $"Response Generated at: {DateTime.Now}";
        }
    }
}

Now, run the application and visit the URL /api/home/index, and you will see the caching is not working. Every time we hit the endpoint, we get a different response even though we have set the cache duration to 60 seconds. This is because we have set the NoStore Attribute value to true, which means caching is disabled. Now, if you check the Response headers, you will see the Cache-Control header whose value is set to no-store, as shown in the image below.

Response Caching in ASP.NET Core Application with Examples

Example: Caching with VaryByHeader

This uses the VaryByHeader property to serve different responses based on the value of the User-Agent or Accept-Language or any other request header. Let us understand this with an example. Please modify the Home Controller as follows:

using Microsoft.AspNetCore.Mvc;
namespace FiltersDemo.Controllers
{
    [Route("api/{controller}/{action}")]
    [ApiController]
    public class HomeController : ControllerBase
    {
        [HttpGet]
        [ResponseCache(Duration = 60, VaryByHeader = "User-Agent")]
        public string Index()
        {
            return $"Response Generated at: {DateTime.Now}";
        }
    }
}
Example: Caching by Location

By default, the Caching is saved on both client and server side. Suppose we want to store the cache response on the client side only. Then, we need to use the Location property and set where we want to store the response cache using the ResponseCacheLocation enum. The possible values for this enum are as follows.

ResponseCacheLocation

ResponseCacheLocation.Client specifies that the response should be cached only on the client side, and in this case, it will set the Cache-Control header to Private. So, modify the Home Controller as follows:

using Microsoft.AspNetCore.Mvc;
namespace FiltersDemo.Controllers
{
    [Route("api/{controller}/{action}")]
    [ApiController]
    public class HomeController : ControllerBase
    {
        [HttpGet]
        [ResponseCache(Duration = 60, Location = ResponseCacheLocation.Client, VaryByHeader = "User-Agent")]
        public string Index()
        {
            return $"Response Generated at: {DateTime.Now}";
        }
    }
}
Example: Caching with VaryByQueryKeys

ASP.NET Core also supports varying the cached response by the values of specified query string keys.

using Microsoft.AspNetCore.Mvc;
namespace FiltersDemo.Controllers
{
    [Route("api/{controller}/{action}")]
    [ApiController]
    public class HomeController : ControllerBase
    {
        [HttpGet]
        [ResponseCache(Duration = 60, VaryByQueryKeys = new[] { "page" })]
        public string Index(int page)
        {
            return $"Response Generated at: {DateTime.Now}, for Page Number: {page}";
        }
    }
}

In this case, the cached output of the Index action method would differ depending on the value of the page query string parameter. This means if a user requests Index?page=1 and then another requests Index?page=2, they will each get a different cached response specific to the page they requested.

Example: Custom Cache Profile in ASP.NET Core

In Startup.cs or Program.cs class file, we can define cache profiles. These profiles allow us to set common caching settings that can be referenced by name in the ResponseCacheAttribute. Let us modify the Program class as follows:

using Microsoft.AspNetCore.Mvc;

namespace FiltersDemo
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Add services to the container.

            builder.Services.AddControllers(options =>
            {
                options.CacheProfiles.Add("Default60", new CacheProfile()
                {
                    Duration = 30
                });
                options.CacheProfiles.Add("NoCache", new CacheProfile()
                {
                    Location = ResponseCacheLocation.None,
                    NoStore = true
                });
            });


            builder.Services.AddResponseCaching();
            // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
            builder.Services.AddEndpointsApiExplorer();
            builder.Services.AddSwaggerGen();

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (app.Environment.IsDevelopment())
            {
                app.UseSwagger();
                app.UseSwaggerUI();
            }

            app.UseHttpsRedirection();
            app.UseResponseCaching();
            app.UseAuthorization();

            app.MapControllers();

            app.Run();
        }
    }
}

Here, we have configured two cache profiles (Default60 and NoCache), as shown in the below image:

Custom Cache Profile

Next, modify the Home Controller as follows:

using Microsoft.AspNetCore.Mvc;
namespace FiltersDemo.Controllers
{
    [ApiController]
    public class HomeController : ControllerBase
    {
        [HttpGet]
        [Route("api/Index")]
        [ResponseCache(CacheProfileName = "Default60")]
        public string Index()
        {
            return $"Index Response Generated at: {DateTime.Now}";
        }

        [HttpGet]
        [Route("api/Privacy")]
        [ResponseCache(CacheProfileName = "NoCache")]
        public string Privacy()
        {
            return $"Privacy Response Generated at: {DateTime.Now}";
        }
    }
}
When to use Response Caching in ASP.NET Core?

Using response caching in ASP.NET Core is a strategy to optimize the performance of a web application by reducing the load on the server and improving response times for the client. However, it’s important to understand when it’s appropriate to use response caching. Here are some scenarios where response caching is beneficial:

  • Static Content: For content that doesn’t change frequently, like CSS files, JavaScript files, images, or static HTML pages. Caching such resources reduces the number of requests to the server.
  • Stable Content: Content that changes infrequently and remains the same across different user requests, such as blog posts, news articles, or product descriptions. Depending on how often they are updated, these can be cached for a period.
  • Read-Heavy and Infrequently Updated Data: In scenarios where data is read frequently but updated infrequently, such as reference data, caching can significantly improve performance.
  • High Traffic, Low Change APIs: Endpoints that are called frequently but whose results change infrequently are ideal candidates for caching.
  • Expensive Operations: If certain requests involve complex and resource-intensive database queries but yield results that don’t change often, caching the responses can reduce server load and improve responsiveness.
  • Publicly Accessible Information: Public content that doesn’t require user-specific customization or authentication, such as public blog entries, news, or product catalogs.
  • Content with Predictable Update Patterns: You can consider caching the data if the content updates on a predictable schedule. For example, a weather forecasting service might update its data every hour, making it a good candidate for caching with an appropriate cache expiration time.

However, there are scenarios where response caching should be used cautiously or avoided:

  • User-Specific Content: Content specific to individual users, like user profiles or personalized recommendations, should generally not be cached. If cached improperly, it might lead to privacy issues or incorrect data being served to users.
  • Highly Dynamic Content: Pages that change frequently, such as real-time stock prices or live sports scores, are not good candidates for caching.
  • Sensitive Information: Any response containing sensitive or personal data should be carefully considered before caching to avoid unauthorized access.
  • When Freshness is Critical: In cases where the most up-to-date information is crucial, like in a dashboard showing real-time statistics, caching might not be suitable.
  • Under Rapid Development: During active development phases, where content changes frequently, caching can lead to confusion and extra overhead in ensuring developers see the most recent changes.
  • Write-Intensive Operations: For actions that involve creating, updating, or deleting data (such as POST, PUT, PATCH, and DELETE HTTP methods), caching is typically not used because the focus is on data modification rather than data retrieval.
  • Limited Resources: If your server has limited memory and you’re considering in-memory caching, you may need to evaluate whether the application has enough memory to handle the cached data without affecting overall performance.

Remember that while response caching can significantly improve the performance of your application, it may not be suitable for all scenarios. There are situations where caching can lead to incorrect or undesirable behavior, such as when serving user-specific data that should not be cached or when dealing with content that changes rapidly.

In the next article, I will discuss the Authorization Filter in ASP.NET Core MVC Application. Here, in this article, I try to explain Response Caching in ASP.NET Core Application with Examples. I hope you enjoy this Response Caching in ASP.NET Core article.

Leave a Reply

Your email address will not be published. Required fields are marked *