fastapi: `List[...]` as type for a `Form` field doesn't work as expected
Opening a new issue since no action has been taken on https://github.com/tiangolo/fastapi/issues/842 for more than a month.
Looks like we have an issue on how fastapi handles lists of elements passed as Form parameter (so anything that is type hinted List[...] = Form(...)). The issue doesn’t arise when they are defined as Query parameters.
After some digging I managed to get to the root of the problem, which is how we pass the parameters when making an api call. fastapi indeed correctly understands that the input needs to be a list of elements, but doesn’t parse the list if it comes from a single field.
Example using Query, works as expected
@app.post("/")
def api_test(l: List[int] = Query(...)) -> List[int]:
return l
Api request generated using the swagger UI
curl -X 'GET' \
'http://localhost:5009/?l=1&l=2' \
-H 'accept: application/json'
note how the passed values are l=1&l=2
[
1,
2
]
Example using Form, doesn’t work as expected
@app.post("/")
def api_test(l: List[int] = Form(...)) -> List[int]:
return l
Api request generated using the swagger UI
curl -X 'POST' \
'http://localhost:5009/' \
-H 'accept: application/json' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'l=1,2'
note how the passed values are l=1,2
{
"detail": [
{
"loc": [
"body",
"l",
0
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}
Manual api request
curl -X 'POST' \
'http://localhost:5009/' \
-H 'accept: application/json' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'l=1' \
-d 'l=2'
Now the values are passed as -d 'l=1' and -d 'l=2'
[
1,
2
]
I am fairly sure that l=1,2 should be an accepted way of passing a list of values as parameter, but fastapi doesn’t seem to like it. Also, if this isn’t the case, the Swagger UI doesn’t produce the correct curl requests for lists given as Form parameters.
Packages:
fastapi:0.66.0python:3.7.4
Let me know if you need other details!
Thank you for coming to my TED talk
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Reactions: 14
- Comments: 17 (5 by maintainers)
~@HitLuca @uricholiveira probably the miss here is that FastAPI/Pydantic need to use the
explodekeyword in the OpenAPI specification. It looks like the default issimplein the OpenAPI spec which yields the CSV form.~EDIT - this is a bug in swagger-ui see https://github.com/swagger-api/swagger-js/issues/2713
That linked doc talks about query parameters but there is a mention of it when describing request bodies too. Looks like the same rules apply.
This worked perfectly @phillipuniverse!
I modified it slightly to use the walrus operator (my first time using the walrus operator, in fact)
@falkben I couldn’t help myself, here’s the fully dynamic version of my monkeypatch!!
Yup, exactly! The key part to my above code is to get the actual OpenAPI schema from Pydantic, not just a reference to it. The schema comes back in the 2nd part of the tuple in the
field_schemacall:Once you’ve got that it’s trivial to determine the array properties. Shouldn’t be too bad to refactor that to a recursive version either.
Let me know if that works for you!
@falkben looking at this again, I think that there is a bug in Swagger UI. According to the ‘Form Data’ section of describing request bodies, it says:
So the default should work the exact same as query parameters. If there are changes to code, it should be within Swagger UI, not in FastAPI or Pydantic (although perhaps FastAPI could make it easier to extend the
encoding).I messed around with a very rough way to override this with monkeypatching:
You would monkeypatch that in the same sort of place you instantiate
FastAPI, or just any time before you invoke theapp.openapi()function.This works great with an endpoint defined like:
The annoying part in the spec is that you have to specify the “encoding” on a per-request-property basis. But you could use the given
body_fieldto look up the properties in the schema that are lists and ensure all of those properties have the rightencodingset on the operation.Hope that helps!
This is also a problem with
Tuple, not justList. Additionally, now in FastAPI 0.68.0 (but not 0.67.0), the Swagger UI doesn’t load for endpoints withFormparameters of typeTuple. See #3665.