2020-08-26 18:04:57 +08:00
|
|
|
from functools import wraps
|
|
|
|
|
from typing import Callable, Optional, Type
|
|
|
|
|
|
|
|
|
|
from fastapi_cache import FastAPICache
|
2020-10-16 16:55:33 +08:00
|
|
|
from fastapi_cache.coder import Coder
|
2020-08-26 18:04:57 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def cache(
|
|
|
|
|
expire: int = None,
|
2020-10-16 16:55:33 +08:00
|
|
|
coder: Type[Coder] = None,
|
|
|
|
|
key_builder: Callable = None,
|
2020-08-26 18:04:57 +08:00
|
|
|
namespace: Optional[str] = "",
|
|
|
|
|
):
|
|
|
|
|
"""
|
|
|
|
|
cache all function
|
|
|
|
|
:param namespace:
|
|
|
|
|
:param expire:
|
|
|
|
|
:param coder:
|
|
|
|
|
:param key_builder:
|
|
|
|
|
:return:
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def wrapper(func):
|
|
|
|
|
@wraps(func)
|
|
|
|
|
async def inner(*args, **kwargs):
|
2020-10-16 16:55:33 +08:00
|
|
|
nonlocal coder
|
|
|
|
|
nonlocal expire
|
|
|
|
|
nonlocal key_builder
|
|
|
|
|
|
|
|
|
|
coder = coder or FastAPICache.get_coder()
|
|
|
|
|
expire = expire or FastAPICache.get_expire()
|
|
|
|
|
key_builder = key_builder or FastAPICache.get_key_builder()
|
2020-10-08 15:10:34 +08:00
|
|
|
request = kwargs.get("request")
|
2020-08-26 18:04:57 +08:00
|
|
|
backend = FastAPICache.get_backend()
|
|
|
|
|
cache_key = key_builder(func, namespace, *args, **kwargs)
|
2020-10-08 15:10:34 +08:00
|
|
|
ttl, ret = await backend.get_with_ttl(cache_key)
|
|
|
|
|
if not request:
|
|
|
|
|
if ret is not None:
|
|
|
|
|
return coder.decode(ret)
|
|
|
|
|
ret = await func(*args, **kwargs)
|
|
|
|
|
await backend.set(cache_key, coder.encode(ret), expire or FastAPICache.get_expire())
|
|
|
|
|
return ret
|
2020-08-26 18:04:57 +08:00
|
|
|
|
|
|
|
|
if request.method != "GET":
|
|
|
|
|
return await func(request, *args, **kwargs)
|
|
|
|
|
if_none_match = request.headers.get("if-none-match")
|
|
|
|
|
if ret is not None:
|
|
|
|
|
response = kwargs.get("response")
|
|
|
|
|
if response:
|
|
|
|
|
response.headers["Cache-Control"] = f"max-age={ttl}"
|
|
|
|
|
etag = f"W/{hash(ret)}"
|
|
|
|
|
if if_none_match == etag:
|
|
|
|
|
response.status_code = 304
|
|
|
|
|
return response
|
|
|
|
|
response.headers["ETag"] = etag
|
|
|
|
|
return coder.decode(ret)
|
|
|
|
|
|
2020-10-08 15:10:34 +08:00
|
|
|
ret = await func(*args, **kwargs)
|
|
|
|
|
await backend.set(cache_key, coder.encode(ret), expire or FastAPICache.get_expire())
|
2020-08-26 18:04:57 +08:00
|
|
|
return ret
|
|
|
|
|
|
|
|
|
|
return inner
|
|
|
|
|
|
|
|
|
|
return wrapper
|