I've been using FastAPI to create an HTTP based API. It currently supports JSON encoded parameters, but I'd also like to support form-urlencoded
(and ideally even form-data
) parameters at the same URL.
Following on Nikita's answer I can get separate urls working with:
from typing import Optional
from fastapi import FastAPI, Body, Form, Depends
from pydantic import BaseModel
class MyItem(BaseModel):
id: Optional[int] = None
txt: str
@classmethod
def as_form(cls, id: Optional[int] = Form(None), txt: str = Form(...)) -> 'MyItem':
return cls(id=id, txt=txt)
app = FastAPI()
@app.post("/form")
async def form_endpoint(item: MyItem = Depends(MyItem.as_form)):
print("got item =", repr(item))
return "ok"
@app.post("/json")
async def json_endpoint(item: MyItem = Body(...)):
print("got item =", repr(item))
return "ok"
and I can test these using curl
by doing:
curl -X POST "http://localhost:8000/form" -d 'txt=test'
and
curl -sS -X POST "http://localhost:8000/json" -H "Content-Type: application/json" -d '{"txt":"test"}'
It seems like it would be nicer to have a single URL that accepts both content-types and have the model parsed out appropriately. But the above code currently fails with either:
{"detail":[{"loc":["body","txt"],"msg":"field required","type":"value_error.missing"}]}
or
{"detail":"There was an error parsing the body"}
if I post to the "wrong" endpoint, e.g. form encoding posted to /json
.
For bonus points; I'd also like to support form-data
encoded parameters as it seems related (my txt
can get rather long in practice), but might need to turn it into another question if it's sufficiently different.
When you're building APIs, FastAPI will be a better choice than Flask, especially when microservices are taken into consideration. The only argument for choosing Flask in this case would be if your organization already has a lot of tooling built around that framework.
About "Form Fields" FastAPI will make sure to read that data from the right place instead of JSON. Data from forms is normally encoded using the "media type" application/x-www-form-urlencoded .
FastAPI can't route based on Content Type, you'd have to check that in the request and parse appropriately:
@app.post('/')
async def route(req: Request) -> Response:
if req.headers['Content-Type'] == 'application/json':
item = MyItem(** await req.json())
elif req.headers['Content-Type'] == 'multipart/form-data':
item = MyItem(** await req.form())
elif req.headers['Content-Type'] == 'application/x-www-form-urlencoded':
item = MyItem(** await req.form())
return Response(content=item.json())
There seems to be an open issue on GitHub regarding this functionality
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With