Problem
@cache(vary=[...]) (#268, PR #309) keys the cache on each listed request header's raw value, lower-cased name and trimmed value only. Header values come from the client, so every distinct value becomes its own entry: Accept-Language: en-US,en;q=0.9 and Accept-Language: en are two entries even when the route only supports en and zh-TW. Reducing a header to the values the route actually serves currently needs a custom key_builder that calls build_cache_key (#264) and leaves out vary= for that header. The docs show this in "Varying on request headers".
Proposal
Let vary also accept a mapping from header name to a normaliser:
def locale(value: str) -> str:
return "zh-TW" if value.lower().startswith("zh") else "en"
@cache(ttl=60, vary={"Accept-Language": locale})
- The normaliser receives the trimmed value (empty when missing) and returns the string that goes into the key. It could also return an
int, as build_cache_key accepts.
- The list form keeps working and keeps its keys. A header mapped to
None behaves like the list form.
- The
Vary response header is unchanged: a downstream cache still sees the raw header.
invalidate(vary=...) must apply the same normaliser so it deletes the right entry.
- An allow-list (
{"Accept": ["application/json", "text/html"]}, with anything else mapping to a default) could be sugar on top. Decide whether it is worth a second form.
Additive. Routes without a mapping keep their keys.
Problem
@cache(vary=[...])(#268, PR #309) keys the cache on each listed request header's raw value, lower-cased name and trimmed value only. Header values come from the client, so every distinct value becomes its own entry:Accept-Language: en-US,en;q=0.9andAccept-Language: enare two entries even when the route only supportsenandzh-TW. Reducing a header to the values the route actually serves currently needs a customkey_builderthat callsbuild_cache_key(#264) and leaves outvary=for that header. The docs show this in "Varying on request headers".Proposal
Let
varyalso accept a mapping from header name to a normaliser:int, asbuild_cache_keyaccepts.Nonebehaves like the list form.Varyresponse header is unchanged: a downstream cache still sees the raw header.invalidate(vary=...)must apply the same normaliser so it deletes the right entry.{"Accept": ["application/json", "text/html"]}, with anything else mapping to a default) could be sugar on top. Decide whether it is worth a second form.Additive. Routes without a mapping keep their keys.