Package: chocs-middleware.cache
Chocs-cacheCache middleware for chocs library.FeaturesEasy integrationSupport for ETagsSupport for conditional request headersif-none-match,if-matchBuilt-in in-memory cache storage for debugging and testing purposesAutomatic cache revalidationInstallationWith pip,pipinstallchocs-middleware.cacheor through poetrypoetryaddchocs-middleware.cacheUsageSimple cacheThe following example shows the simplest usage of cache mechanism with a custom
in-memory cache storage.importchocsfromchocsimportHttpRequest,HttpResponsefromchocs_middleware.cacheimportICacheStorage,CacheItem,CacheMiddlewareclassMemoryCache(ICacheStorage):"""Custom cache storage that uses memory to store the cache items.In production, this should use Redis or other cache databases used by your application."""def__init__(self):self._cache={}defget(self,cache_id:str)->CacheItem:returnself._cache[cache_id]defset(self,item:CacheItem)->None:self._cache[item.id]=itemapp=chocs.Application(CacheMiddleware(MemoryCache()))@app.get("/users/{user_id}",cache_expiry=10)defget_user(request:HttpRequest)->HttpResponse:"""cache_expiry enables caching for the returned response and keepsit alive for 10 seconds."""returnHttpResponse("Bob Bobber",200)ETag based cacheTo make use of e-tags simply return the e-tag header in the response, like in the example below:importchocsfromchocsimportHttpRequest,HttpResponsefromchocs_middleware.cacheimportCacheMiddleware,InMemoryCacheStorageapp=chocs.Application(CacheMiddleware(InMemoryCacheStorage()))@app.get("/users/{user_id}",cache_expiry=10)defget_user(request:HttpRequest)->HttpResponse:returnHttpResponse("Bob Bobber",headers={"etag":'"etag-1"',# your custom etag value})Keep in mind e-tag values MUST BE unique per REST endpoint and per content type to avoid cache collisions.Using cache varyTo allow the cache system better understand your intention it is recommended to use thecache_varyattribute.
You can read more about theVaryheaderhereimportchocsfromchocsimportHttpRequest,HttpResponsefromchocs_middleware.cacheimportCacheMiddleware,InMemoryCacheStorage# you can specify cache vary globally when initialising middlewareapp=chocs.Application(CacheMiddleware(InMemoryCacheStorage(),cache_vary=set("accept-language")))# or you can specify cache vary per endpoint@app.get("/users/{user_id}",cache_expiry=10,cache_vary=("accept-language","x-custom-header"))defget_user(request:HttpRequest)->HttpResponse:returnHttpResponse("Bob Bobber")Specifying cache controlYou can also specify the type of cache by setting thecache_controlattribute topublicorprivate.
You can read more about cache-controlhereimportchocsfromchocsimportHttpRequest,HttpResponsefromchocs_middleware.cacheimportCacheMiddleware,InMemoryCacheStorageapp=chocs.Application(CacheMiddleware(InMemoryCacheStorage()))# cache control can only be specified in the route definition.@app.get("/users/{user_id}",cache_expiry=10,cache_control="private")defget_user(request:HttpRequest)->HttpResponse:returnHttpResponse("Bob Bobber")Conditional request supportThis cache system supports conditional requests headersif-none-matchandif-matchin a limited manner.
This means values passed inif-none-matchandif-matchheaders will be treated as a single value.Cache revalidationThe middleware providesICollectableCacheinterfaces that you can use to implement yourcache re-validation. It might be just a simple mechanism that just deletes the stale cache, but
there are no limitations and more powerful systems might be built on the top of the interface.The following diagram represents the way how to cache revalidation works in the middleware:The following example shows the simplest implementation of cache re-validation:fromchocsimportApplication,HttpRequest,HttpResponsefromchocs_middleware.cacheimportICollectableCacheStorage,CacheItem,CacheMiddlewareclassCacheStorage(ICollectableCacheStorage):def__init__(self):self._storage={}defset(self,item:CacheItem)->None:self._storage[item.id]=itemdefget(self,item_id:str)->CacheItem:returnself._storage[item_id]defcollect(self,item:CacheItem)->None:delself._storage[item.id]app=Application(CacheMiddleware(CacheStorage))@app.get("/users/{user_id}",cache_expiry=10)defget_test(req:HttpRequest)->HttpResponse:user_id=req.path_parameters.get("user_id")returnHttpResponse(f"user{user_id}",headers={"etag":f"{user_id}"})@app.patch("/users/{user_id}",cache=True)# cache attribute enables cache middleware for the endpoint, to collect stale datadefget_test(req:HttpRequest)->HttpResponse:"""Once this function generate a successful response and cache exists for the given user CacheStorage.collect is called"""returnHttpResponse("{user data}")