Caching the response of an API speeds up the API endpoint because the code that generates the response is not called, but rather fetched from memory. This is especially helpful for API’s where the response is time-consuming to retrieve but the response does not change.
To add a response cache, you need to declare that caching is enabled, then specify the cache parameters for each of your API endpoints.
STEP 1: ENABLE CACHING IN Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddResponseCaching();
var app = builder.Build();
app.UseHttpsRedirection();
// UseCors must be called before UseResponseCaching
// app.UseCors();
app.UseResponseCaching();
app.UseAuthorization();
app.MapControllers();
app.Run();
It is important that you call UseResponseCaching AFTER you have called UseCors.
STEP 2: DEFINE THE CACHE PROPERTIES FOR YOUR API ENDPOINT
To declare the cache properties for an endpoint, use the [ResponseCache] attribute:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace MyCode.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class MyController : ControllerBase
{
public MyController()
{
}
[HttpGet]
[ResponseCache(Duration=10)]
public string? GetCatFact()
{
// Pseudocode. Call something that will return a random cat fact
// Insert your own code logic here:
CatModel? catFact = _httpRepository.GetAsync<CatModel>("https://catfact.ninja/fact").Result;
return catFact?.Fact;
}
}
}
In the example above, the ResponseCache attribute will cache the response for 10 seconds unconditionally.
The first request will call the underlying code, but the next requests are fetched from the memory cache, until the cache expires after 10 seconds. The response header will now have a max-age of 10, indicating the cache duration:

VARY THE CACHE BY HEADERS OR QUERYSTRING
The your API endpoint response is controlled using querystrings, you should add those as variation:
[HttpGet]
[ResponseCache(Duration=30, VaryByQueryKeys=new[] {"id", "search"})]
public string? GetCatFact([FromQuery] int id, [FromQuery] string search)
{
// Still pseudo code, add your own logic
CatModel? catFact = _httpRepository.GetAsync<CatModel>("https://catfact.ninja/fact").Result;
return catFact?.Fact + id + search;
}
The response will very depending of the value of the querystrings. Use VaryByHeaders to vary by headers.
DEFINING CACHE PROFILES
Instead of defining the cache individually per endpoint, you can create cache profiles that can be reused. Go to the Program.cs and add the profiles to the AddControllers method:
builder.Services.AddControllers(options =>
{
options.CacheProfiles.Add("Default",
new CacheProfile() { Duration = 10 }
);
options.CacheProfiles.Add("Client",
new CacheProfile() { Location = ResponseCacheLocation.Client, Duration = 10 }
);
}
);
Then use the profile name in the API endpoint. You can still decorate the endpoint with VaryByHeader/VaryByQueryKeys:
[HttpGet]
[ResponseCache(CacheProfileName="Default")]
public string? GetCatFact()
{
CatModel? catFact = _httpRepository.GetAsync<CatModel>("https://catfact.ninja/fact").Result;
return catFact?.Fact;
}
[HttpGet("2")]
[ResponseCache(CacheProfileName="Client", VaryByQueryKeys=new[] {"id", "search"})]
public string? GetCatFact2([FromQuery] int id, [FromQuery] string search)
{
CatModel? catFact = _httpRepository.GetAsync<CatModel>("https://catfact.ninja/fact").Result;
return catFact?.Fact + id + search;
}
That’s it. You are now a caching expert. Happy coding.
MORE TO READ:
- Response caching in ASP.NET Core from Microsoft
- Response Caching Middleware in ASP.NET Core from Microsoft
- .NET API CORS: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header.NET API from briancaos
- C# .net Swagger API Versioning – Show versions in your Swagger page from briancaos