ResponseCache
As we discussed in the introduction article for this chapter, all modern browsers comes with a lot of caching functionality built-in. They will always try to detect static stuff, like images and style-sheets, and cache it for the next request made by the user. This doesn't apply to the actual page though, because the browser has no way of knowing whether your server will serve the exact same response on the next request or not. That doesn't mean that caching of the page is not possible, it just means that you have to guide the browser. For that purpose, ASP.NET MVC comes with a technique called ResponseCache.
ResponseCache, sometimes referred to as HTTP-based response caching, is most commonly applied directly to the action of a Controller, to let the browser know that it can safely cache the response for a specific amount of time. So, if the result of your Controller action is always static, or at least remain the same for a specific amount of time, you can tell the browser to cache it like this:
public class CacheController : Controller
{
[ResponseCache(Duration = 120)]
public IActionResult Index()
{
return View();
}
}
With that in place, the browser requesting the page will be given permission to cache the response of the request for 2 minutes (120 seconds). This will be noticeable in the response headers, which you can inspect in the developer tools of your browser (e.g. DevTools in Google Chrome) where you will see a response header like this:
Cache-Control: public,max-age=120
But please notice one very important thing: This is just a recommendation made to the browser. At any time, the browser is free to ignore this recommendation and re-request the page. This will, for instance, likely happen if the user presses the Refresh button of the browser instead of simply navigating to the cached page by clicking a link.
Vary options
In the above example, the page will be cached no matter what, but in many situations, your page might change depending on how the user accesses it. For instance, you might use parameters in the query string to serve different content based on how the user requests it, like this:
/Users/Details?id=1
/Users/Details?id=2
....
For a situation like that, you want to make sure that the details page for user #1 is not cached and displayed for user #2. Fortunately for us, the ResponseCache directive comes with options for varying the content based on the query string, but it requires the so-called ResponseCache middleware. In other words, you need to add this NuGet package to your project and then reference it in your Startup.cs file, where you also have to add to lines of code, like this:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddResponseCaching();
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseResponseCaching();
app.UseMvcWithDefaultRoute();
}
}
The lines in question are the ones with services.AddResponseCaching() and app.UseResponseCaching(). With that in place, you can now use the VaryByQueryKeys parameter:
[ResponseCache(Duration = 120, VaryByQueryKeys = new[] { "id" })]
public IActionResult Details(int id)
{
...
Now, the requested page will only be cached if the id parameter is the same. You can specify multiple parameters to vary by, or you can use the asterisk if you want to vary by all possible parameters:
[ResponseCache(Duration = 120, VaryByQueryKeys = new[] { "*" })]
VaryByHeader
You can also vary the content based on headers sent to the server from the client, e.g. "User-Agent", which identifies a client (a webbrowser). It can be used like this:
[ResponseCache(Duration = 120, VaryByHeader = "User-Agent" )]
Summary
The ResponseCache directive allows you to instruct the visiting client in how it may or may not cache your pages. However, please keep in mind that as long as this is just a recommendation to each client, the amount of resources and bandwidth saved will not be as great as if you had the full control. We'll talk more about this in the next article.