diff --git a/examples/in_memory/main.py b/examples/in_memory/main.py index c43dce3..516cdc7 100644 --- a/examples/in_memory/main.py +++ b/examples/in_memory/main.py @@ -114,6 +114,15 @@ async def uncached_put(): put_ret = put_ret + 1 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") @cache(namespace="test", expire=5, injected_dependency_namespace="monty_python") diff --git a/fastapi_cache/coder.py b/fastapi_cache/coder.py index 904fc69..4ce9432 100644 --- a/fastapi_cache/coder.py +++ b/fastapi_cache/coder.py @@ -15,12 +15,15 @@ from typing import ( import pendulum from fastapi.encoders import jsonable_encoder -from pydantic import BaseConfig, ValidationError, fields from starlette.responses import JSONResponse from starlette.templating import ( _TemplateResponse as TemplateResponse, # pyright: ignore[reportPrivateUsage] ) + +class ModelField: + pass + _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 # given type, do make sure that the subclass provides its own class # attribute for this cache. - _type_field_cache: ClassVar[Dict[Any, fields.ModelField]] = {} + _type_field_cache: ClassVar[Dict[Any, ModelField]] = {} @overload @classmethod @@ -89,18 +92,6 @@ class Coder: """ 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 diff --git a/fastapi_cache/decorator.py b/fastapi_cache/decorator.py index 4c7cf33..40a1920 100644 --- a/fastapi_cache/decorator.py +++ b/fastapi_cache/decorator.py @@ -72,7 +72,7 @@ def _uncacheable(request: Optional[Request]) -> bool: Returns true if: - Caching has been disabled globally - 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(): @@ -81,7 +81,7 @@ def _uncacheable(request: Optional[Request]) -> bool: return False if request.method != "GET": return True - return request.headers.get("Cache-Control") in ("no-store", "no-cache") + return request.headers.get("Cache-Control") == "no-store" def cache( @@ -182,7 +182,7 @@ def cache( ) 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) to_cache = coder.encode(result) diff --git a/tests/test_decorator.py b/tests/test_decorator.py index fefa182..b688569 100644 --- a/tests/test_decorator.py +++ b/tests/test_decorator.py @@ -99,10 +99,10 @@ def test_pydantic_model() -> None: def test_non_get() -> None: with TestClient(app) as client: - response = client.put("/uncached_put") + response = client.put("/cached_put") assert "X-FastAPI-Cache" not in response.headers assert response.json() == {"value": 1} - response = client.put("/uncached_put") + response = client.put("/cached_put") assert "X-FastAPI-Cache" not in response.headers assert response.json() == {"value": 2} @@ -112,3 +112,26 @@ def test_alternate_injected_namespace() -> None: response = client.get("/namespaced_injection") assert response.headers.get("X-FastAPI-Cache") == "MISS" 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}