HTTP caching is a mechanism that shines in RESTful APIs. A
response on a detail resource can be augmented with the
ETag header, which
essentially is a “version” of a resource. Every modification of the resource
leads to a new version.
This can be leveraged in combination with the
If-None-Match request header,
kwown as conditional requests.
The client includes the
ETag value(s) in this header, and if the current
version of the resource has the same
ETag value, then the server replies
HTTP 304 response, indicating that the content has not changed.
This allows consumers to send less data over the wire without having to perform extra requests if they keep resources in their own cache.
See the ETag on MDN documentation for the full specification.
Implementing conditional requests
Two actions are needed to implement this in API implementations:
Add the model mixin to save the ETag value
This enables us to perform fast lookups and avoid calculating the ETag value
over and over again. It happens automatically on
save of a model instance.
1from vng_api_common.caching import ETagMixin 2 3 4class MyModel(ETagMixin, models.Model): 5 pass
If you’re doing
you need to take care of setting/calculating the value yourself.
Decorate the viewset
Decorating the viewset retrieve ensures that the
ETag header is set,
the conditional request handling (HTTP 200 vs. HTTP 304) is performed and the
extra methods/headers show up in the generated API schema.
1from rest_framework import viewsets 2from vng_api_common.caching import conditional_retrieve 3 4from .models import MyModel 5from .serializers import MyModelSerializer 6 7 8@conditional_retrieve() 9class MyModelViewSet(viewsets.ReadOnlyViewSet): 10 queryset = MyModel.objects.all() 11 serializer_class = MyModelSerializer
Facilitate HTTP caching mechanisms.
This package contains the tooling required to (efficiently) apply and use ETag HTTP headers.
On a request level, the API will return HTTP 304 statuses if the client has an up to date version of the resource. It will reply with a HTTP 200 otherwise, including the ETag header.
This package provides a model mixin to save the ETag header value to the db, and a decorator to enable conditional requests on viewsets. The rest are implementation details.
- class vng_api_common.caching.ETagMixin(*args, **kwargs)
Automatically calculate the (new) ETag value on save.
Note that the signal receivers in
vng_api_common.caching.signalsare responsible for (cached) _etag value invalidation.
- calculate_etag_value() str
Calculate and save the ETag value.
- vng_api_common.caching.calculate_etag(instance: Model) str
Calculate the MD5 hash of a resource representation in the API.
The serializer for the model class is retrieved, and then used to construct the representation of the instance. Then, the representation is rendered to camelCase JSON, after which the MD5 hash is calculated of this result.
- vng_api_common.caching.conditional_retrieve(action='retrieve', etag_field='_etag', extra_depends_on: Optional[Set[str]] = None)
Decorate a viewset to apply conditional GET requests.
The decorator patches the handler to calculate and emit the required ETag-related headers. Additionally, it sets up the dependency tree for the exposed resource so that the ETag value can be recalculated when the resource or relevant related resources are modified, resulting in an updated ETag value. This is introspected through the specified viewset serializer class.
action – The viewset action to decorate
etag_field – The model field containing the (cached) ETag value
extra_depends_on – A set of additional field names the ETag value calculation depends on. Normally, this is inferred from the serializer, but in some cases ( .e.g.
SerializerMethodField) this cannot be automatically detected. These fields will be added to the automatically introspected serializer relations.