mirror of
https://github.com/long2ice/fastapi-cache.git
synced 2026-03-24 20:47:54 +00:00
Merge pull request #425 from PaleNeutron/fix-no-cache
fix #424, no-cache should store the result to cache
This commit is contained in:
@@ -114,6 +114,15 @@ async def uncached_put():
|
|||||||
put_ret = put_ret + 1
|
put_ret = put_ret + 1
|
||||||
return {"value": put_ret}
|
return {"value": put_ret}
|
||||||
|
|
||||||
|
put_ret2 = 0
|
||||||
|
|
||||||
|
@app.get("/cached_put")
|
||||||
|
@cache(namespace="test", expire=5)
|
||||||
|
async def cached_put():
|
||||||
|
global put_ret2
|
||||||
|
put_ret2 = put_ret2 + 1
|
||||||
|
return {"value": put_ret2}
|
||||||
|
|
||||||
|
|
||||||
@app.get("/namespaced_injection")
|
@app.get("/namespaced_injection")
|
||||||
@cache(namespace="test", expire=5, injected_dependency_namespace="monty_python")
|
@cache(namespace="test", expire=5, injected_dependency_namespace="monty_python")
|
||||||
|
|||||||
@@ -15,12 +15,15 @@ from typing import (
|
|||||||
|
|
||||||
import pendulum
|
import pendulum
|
||||||
from fastapi.encoders import jsonable_encoder
|
from fastapi.encoders import jsonable_encoder
|
||||||
from pydantic import BaseConfig, ValidationError, fields
|
|
||||||
from starlette.responses import JSONResponse
|
from starlette.responses import JSONResponse
|
||||||
from starlette.templating import (
|
from starlette.templating import (
|
||||||
_TemplateResponse as TemplateResponse, # pyright: ignore[reportPrivateUsage]
|
_TemplateResponse as TemplateResponse, # pyright: ignore[reportPrivateUsage]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ModelField:
|
||||||
|
pass
|
||||||
|
|
||||||
_T = TypeVar("_T", bound=type)
|
_T = TypeVar("_T", bound=type)
|
||||||
|
|
||||||
|
|
||||||
@@ -69,7 +72,7 @@ class Coder:
|
|||||||
# decode_as_type method and then stores a different kind of field for a
|
# decode_as_type method and then stores a different kind of field for a
|
||||||
# given type, do make sure that the subclass provides its own class
|
# given type, do make sure that the subclass provides its own class
|
||||||
# attribute for this cache.
|
# attribute for this cache.
|
||||||
_type_field_cache: ClassVar[Dict[Any, fields.ModelField]] = {}
|
_type_field_cache: ClassVar[Dict[Any, ModelField]] = {}
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -89,18 +92,6 @@ class Coder:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
result = cls.decode(value)
|
result = cls.decode(value)
|
||||||
if type_ is not None:
|
|
||||||
try:
|
|
||||||
field = cls._type_field_cache[type_]
|
|
||||||
except KeyError:
|
|
||||||
field = cls._type_field_cache[type_] = fields.ModelField(
|
|
||||||
name="body", type_=type_, class_validators=None, model_config=BaseConfig
|
|
||||||
)
|
|
||||||
result, errors = field.validate(result, {}, loc=())
|
|
||||||
if errors is not None:
|
|
||||||
if not isinstance(errors, list):
|
|
||||||
errors = [errors]
|
|
||||||
raise ValidationError(errors, type_)
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ def _uncacheable(request: Optional[Request]) -> bool:
|
|||||||
Returns true if:
|
Returns true if:
|
||||||
- Caching has been disabled globally
|
- Caching has been disabled globally
|
||||||
- This is not a GET request
|
- This is not a GET request
|
||||||
- The request has a Cache-Control header with a value of "no-store" or "no-cache"
|
- The request has a Cache-Control header with a value of "no-store"
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not FastAPICache.get_enable():
|
if not FastAPICache.get_enable():
|
||||||
@@ -81,7 +81,7 @@ def _uncacheable(request: Optional[Request]) -> bool:
|
|||||||
return False
|
return False
|
||||||
if request.method != "GET":
|
if request.method != "GET":
|
||||||
return True
|
return True
|
||||||
return request.headers.get("Cache-Control") in ("no-store", "no-cache")
|
return request.headers.get("Cache-Control") == "no-store"
|
||||||
|
|
||||||
|
|
||||||
def cache(
|
def cache(
|
||||||
@@ -182,7 +182,7 @@ def cache(
|
|||||||
)
|
)
|
||||||
ttl, cached = 0, None
|
ttl, cached = 0, None
|
||||||
|
|
||||||
if cached is None: # cache miss
|
if cached is None or (request is not None and request.headers.get("Cache-Control") == "no-cache") : # cache miss
|
||||||
result = await ensure_async_func(*args, **kwargs)
|
result = await ensure_async_func(*args, **kwargs)
|
||||||
to_cache = coder.encode(result)
|
to_cache = coder.encode(result)
|
||||||
|
|
||||||
|
|||||||
@@ -99,10 +99,10 @@ def test_pydantic_model() -> None:
|
|||||||
|
|
||||||
def test_non_get() -> None:
|
def test_non_get() -> None:
|
||||||
with TestClient(app) as client:
|
with TestClient(app) as client:
|
||||||
response = client.put("/uncached_put")
|
response = client.put("/cached_put")
|
||||||
assert "X-FastAPI-Cache" not in response.headers
|
assert "X-FastAPI-Cache" not in response.headers
|
||||||
assert response.json() == {"value": 1}
|
assert response.json() == {"value": 1}
|
||||||
response = client.put("/uncached_put")
|
response = client.put("/cached_put")
|
||||||
assert "X-FastAPI-Cache" not in response.headers
|
assert "X-FastAPI-Cache" not in response.headers
|
||||||
assert response.json() == {"value": 2}
|
assert response.json() == {"value": 2}
|
||||||
|
|
||||||
@@ -112,3 +112,26 @@ def test_alternate_injected_namespace() -> None:
|
|||||||
response = client.get("/namespaced_injection")
|
response = client.get("/namespaced_injection")
|
||||||
assert response.headers.get("X-FastAPI-Cache") == "MISS"
|
assert response.headers.get("X-FastAPI-Cache") == "MISS"
|
||||||
assert response.json() == {"__fastapi_cache_request": 42, "__fastapi_cache_response": 17}
|
assert response.json() == {"__fastapi_cache_request": 42, "__fastapi_cache_response": 17}
|
||||||
|
|
||||||
|
def test_cache_control() -> None:
|
||||||
|
with TestClient(app) as client:
|
||||||
|
response = client.get("/cached_put")
|
||||||
|
assert response.json() == {"value": 1}
|
||||||
|
|
||||||
|
# HIT
|
||||||
|
response = client.get("/cached_put")
|
||||||
|
assert response.json() == {"value": 1}
|
||||||
|
|
||||||
|
# no-cache
|
||||||
|
response = client.get("/cached_put", headers={"Cache-Control": "no-cache"})
|
||||||
|
assert response.json() == {"value": 2}
|
||||||
|
|
||||||
|
response = client.get("/cached_put")
|
||||||
|
assert response.json() == {"value": 2}
|
||||||
|
|
||||||
|
# no-store
|
||||||
|
response = client.get("/cached_put", headers={"Cache-Control": "no-store"})
|
||||||
|
assert response.json() == {"value": 3}
|
||||||
|
|
||||||
|
response = client.get("/cached_put")
|
||||||
|
assert response.json() == {"value": 2}
|
||||||
|
|||||||
Reference in New Issue
Block a user