An implementation of HTTP Caching in .NET Core and 4.5.2+ for both the client and the server
An implementation of HTTP Caching in .NET Core and 4.5.2+ for both the client and the server
An implementation of HTTP Caching in .NET Core and 4.52+ for HttpClient, ASP.NET Web API, ASP.NET MVC Core and Carter.
This document covers topics below:
There are quite a few changes in the server-side implementation of CacheCow due to shortcomings of previous approach while the client-side mostly remained the same. Hence if you have used CacheCow.Server in the past and you would like to migrate, read the guide on migration further down. CacheCow.Client has some changes which may or may not break your code (depending whether you had used .NET 4.5 packages) which includes:
It is useful to start with concepts but feel free to use it as a reference. If you get bored reading this , jump straight to Getting Started sections or simply browse and run a couple of samples.
HttpClient./api/car/1 is a resource and it might have two representations as JSON or XML. Also the same resource could have two representations in Chinese or Spanish. Headers by which representations will vary is defined by the Vary header from the server.Cache-Control, Vary, ETag and in HTTP 1.0 would include Expires, Last-Modified and Pragma (for the purpose of this article, we include ETag and Last-Modified as part of directives although purely speaking they are not). CacheCow has ICacheDirectiveProvider interface responsible for controlling these headers.Expires header: Defines expiry of a resource in absolute time. Cache-Control header provides richer semantic and supersedes it. CacheCow sets both to support both HTTP 1.0 and 1.1. Last-Modified header: Since its precision is at seconds, its use is not recommended for valiation and instead, it is recommended to use ETag.ETag header: ETag or EntityTag is an opaque string that identifies a version of resource (not representation). If you have a high-precision Last-Modified date, it is better to turn it to ETag by binarising the date (example).TimedEntityTagHeaderValue. It is recommended to construct it with an ETag (due to low precision of DateTime in HTTP Spec's Last-Modified header).ITimedETagExtractor: A CacheCow interface responsible for extracting TimedETag from view models sent back by the API. By default, it checks to see if the ViewModel has implemented ICacheResource, if so it enquires the TimedETag directly. If not, it will resort to serialising the ViewModel to JSON and use its hash as ETag. This can be expensive hence it is suggested to either your ViewModels implementing ICacheResource interface or implement ITimedETagExtractor - plenty of examples in the samples.Last-Modified date supplied. This is known as conditional PUT/PATCH/DELETE. CacheCow supports these scenarios OOB.SELECT COUNT(1), MAX(LastModifiedDate) FROM MyTable). Such queries will be fast and cheap. CacheCow provides ITimedETagQueryProvider interface to preemptively query the backend stores for conditional HTTP calls without load the resources.ITimedETagQueryProvider: This interface allows server implementations to query their back-end and carry out validation against it. This is the best way to have APIs support consistency and the most efficient level of caching.Client scenario is perhaps the most common use case of CacheCow. Most of the concepts discussed above relate to the server-side. Client-side CacheCow has been implemented as a DelegatingHandler and has very few concept counts - most of the complexity of HTTP Caching has been hidden away from you. For the purpose of this guide, we choose an In-Memory storage which is the default.
> install-package CacheCow.ClientHttpClient (piped to a CachingHandler fronted by HttpClientHandler):var client = ClientExtensions.CreateClient();This is simply a helper and you saves you writing a couple of lines of code.
JQuery CDN is a handy little cacheable resource. We make a call twice and check CacheCow header:
const string CacheableResource = "https://code.jquery.com/jquery-3.3.1.slim.min.js";
var response = client.GetAsync(CacheableResource).
ConfigureAwait(false).GetAwaiter().GetResult();
var responseFromCache = client.GetAsync(CacheableResource).
ConfigureAwait(false).GetAwaiter().GetResult();
Console.WriteLine(response.Headers.GetCacheCowHeader().ToString()); // outputs "2.0.0.0;did-not-exist=true"
Console.WriteLine(responseFromCache.Headers.GetCacheCowHeader().ToString()); // outputs "2.0.0.0;did-not-exist=false;retrieved-from-cache=true"As you can see, second time round the resource came from the cache and the request did not even hit the network.
NOTE: In-Memory storage is OK for test scenarios or cases where the load is limited. In many cases you would choose to use Redis storage or you can implement your own if you need to. If you would need an alternative storage not yet supported, feel free to discuss by opening an issue before sending a PR.
From CacheCow 2.0, ASP.NET MVC Core scenarios are supported. Server-side CacheCow has been implemented as a Resource Filter.
> install-package CacheCow.Server.Core.Mvcpublic virtual void ConfigureServices(IServiceCollection services)
{
... // usual startup code
services.AddHttpCachingMvc(); // add HTTP Caching for Core MVC
}HttpCacheFactory attributeProvide the expiry as the first parameter (number of seconds):
public class MyController : Controller
{
[HttpGet]
[HttpCacheFactory(300)]
public IActionResult Get(int id)
{
... // implementation
}
}Here we have set the expiry to
No open issues yet, or sync has not completed.