Fastapi: Issue getting body params without using pydantic model

Created on 8 Mar 2020  路  7Comments  路  Source: tiangolo/fastapi

I've followed the instructions in #895 and in the docs but I must be doing something silly because this doesn't work:

@router.post("/posts", tags=["posts"], status_code=201)
async def create_post(url: str = Body(...)):
    print(url)
    return []

It returns {"detail":[{"loc":["body","url"],"msg":"str type expected","type":"type_error.str"}]}

with the body { "url": "hello there" }

but the following works using a pydantic model:

class CreatePost(BaseModel):
    url: str


@router.post("/posts", tags=["posts"], status_code=201)
async def create_post(body: CreatePost):
    print(body.url)
    return []

Any ideas?

question

Most helpful comment

Thanks @gautamsinghania95 for the suggestion, but I would expect that for most of the use cases people would use a single variable expecting it to be the top body. And having to add additional code to make it work like that would end up being quite cumbersome.

I agree that the fact that it behaves differently with respect to one specific parameter depending on if there are other body parameters or not is not ideal, but here's one of those points where I would trade the simplicity for most use cases in exchange of a little inconsistency for these corner cases.

And either way, both options are achievable with embed=True.

On the other hand, changing the default behavior would break many live apps, for not a very big improvement. So I think I'll leave it as it is.

All 7 comments

Short answer: Adding embed=True to url will work

@router.post("/posts", tags=["posts"], status_code=201)
async def create_post(url: str = Body(..., embed=True)):
    print(url)
    return []

Long answer:

  • With only one variable as the Body, FastAPI thinks of that as describing the whole body
  • In the first scenario, with url as the only Body variable, FastAPI thought of it as describing the whole Body structure
  • Since it was a string, a map failed
  • In the body, instead of { "url": "hello there" }, if you passed "hello there", it would have worked
  • In the second scenario, with body as the only Body variable, FastAPI thought of it as describing the whole Body structure
  • Since it was a Pydantic BaseModel, a map worked

@tiangolo I have a view that the default should be other way round. To have only one variable describe the whole body structure, we must have a flag like whole_body=True added.

Thanks @gautamsinghania95!

I guess that's why the example that I was following works and mine doesn't (2 instead of 1 in body).

I think I agree with you that having the syntax the same on the client whether it's 1 or 2 variables passed is better... more consistent (although I would rather pass one on the client and not use embed on the server - less code). And since the whole point of doing this is to reduce the line count by not having to define a pydantic model, then having the default be the same as with multiple variables makes more sense.

I foresee many backward compatibility issues if this is changed though :P

@dsmurrell agreed. Maybe we can start rolling the parameter like whole_body out and give a deprecated warning message when an embed parameter is used.

This is explained in the docs: https://fastapi.tiangolo.com/tutorial/body-multiple-params/#embed-a-single-body-parameter

If this didn't work the way it does, if you declared a Pydantic model with several attributes they would end up one level deep inside a key.

And it would also make it impossible to receive bodies with lists, or scalar values, like numbers, strings, etc.

Ah, didn't realise there was a reason it was set up like this! Thanks for getting back!

@tiangolo your point is valid for allowing other types of bodies like lists, values, etc. However, I feel that my suggestion for whole_body tag is still valid. This tag could be used for your usecase, and this also helps in a better code structure (instead of making it so that it behaves differently for one variable vs two variables in a function).

Please consider :)

Thanks @gautamsinghania95 for the suggestion, but I would expect that for most of the use cases people would use a single variable expecting it to be the top body. And having to add additional code to make it work like that would end up being quite cumbersome.

I agree that the fact that it behaves differently with respect to one specific parameter depending on if there are other body parameters or not is not ideal, but here's one of those points where I would trade the simplicity for most use cases in exchange of a little inconsistency for these corner cases.

And either way, both options are achievable with embed=True.

On the other hand, changing the default behavior would break many live apps, for not a very big improvement. So I think I'll leave it as it is.

Was this page helpful?
0 / 5 - 0 ratings