Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

supporting both form and json encoded bodys with FastAPI

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.

like image 899
Sam Mason Avatar asked May 18 '20 15:05

Sam Mason


People also ask

Is FastAPI better than flask?

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.

What is FastAPI form?

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 .


1 Answers

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

like image 184
Gabriel Cappelli Avatar answered Nov 15 '22 01:11

Gabriel Cappelli