fastapi-cache icon indicating copy to clipboard operation
fastapi-cache copied to clipboard

Error when using FastAPI request state variable AND also body parameter

Open john-tipper opened this issue 2 years ago • 7 comments

I have a FastAPI POST endpoint which receives a parameter in the request body. A global FastAPI Depends sets a Request state value which I want to retrieve in my method.

The following works fine:

from pydantic import BaseModel
from fastapi import APIRouter, Request


class MyPydanticValue(BaseModel):
    # some properties here


router = APIRouter()

@router.post(
  "/foo"
)
def perform_action(
  my_val: MyPydanticValue
  request: Request
):
  # do something with my_val
  ...

  # and also reference a state value that was set globally elsewhere
  print(request.state.x_some_value) 

FastAPI sees the my_val parameter, recognises that it is a Pedantic model and therefore parses it from the request body.

I now want to cache the function, so I use @cache:

@router.post(
  "/foo"
)
@cache(expire=300)
def perform_action(
  my_val: MyPydanticValue
  request: Request
):
  # do something with my_val
  ...

  #and also reference a state value that was set globally elsewhere
  print(request.state.x_some_value) 

Now I get an exception:

TypeError: perform_action() got multiple values for argument 'my_val'

The fastapi-cache documentation states:

The cache decorator injects dependencies for the Request and Response objects, so that it can add cache control headers to the outgoing response, and return a 304 Not Modified response when the incoming request has a matching If-Non-Match header. This only happens if the decorated endpoint doesn't already list these dependencies already.

My reading of that is that the injection shouldn't happen because I declare a parameter referring to the Request already. I think that's what is happening in the source code for the @cache decorator: https://github.com/long2ice/fastapi-cache/blob/v0.2.1/fastapi_cache/decorator.py#L41-L46. However, I think that my function is indeed actually getting a request parameter being injected by fastapi-cache and then subsequently by FastAPI. Alternatively, something that the cache wrapper is doing is messing up the parsing of the body.

What am I doing wrong - is this a bug? If so, is there a way of working around this? How do I access the state value in the endpoint function whilst also parsing the Pydantic model from the request body?

john-tipper avatar May 24 '23 21:05 john-tipper

You can try install main.

pip install git+https://github.com/long2ice/fastapi-cache.git         

But if the post method you use will not cache, if you want to test, you can modify it to return false. https://github.com/long2ice/fastapi-cache/blob/8945ac7a3a7d1533388a81460afdc2b5f0b1dfc2/fastapi_cache/decorator.py#L82-L83

vvanglro avatar May 25 '23 07:05 vvanglro

I've traced the error to this line: https://github.com/long2ice/fastapi-cache/blob/v0.2.1/fastapi_cache/decorator.py#L152

return await ensure_async_func(request, *args, **kwargs)

Is there a reason why this particular invocation of the function adds a request argument, whereas this does not happen anywhere else in the code? I don't think that request argument belongs in that call. It results in too many arguments being passed to the underlying function.

john-tipper avatar May 25 '23 17:05 john-tipper

@john-tipper Did you ever find a workaround for this? Running into the same thing.

plv avatar Sep 19 '23 22:09 plv

Same thing, can someone answer?

Ninefiveblade avatar Oct 12 '23 16:10 Ninefiveblade

hi any update? can we please open a PR and fix the relevant line?

golankopi avatar Dec 26 '23 08:12 golankopi

I also have the same issue

admo1 avatar Feb 16 '24 14:02 admo1

Try changing your method to GET instead of POST. It worked for me.

ricardordb avatar Mar 07 '24 21:03 ricardordb