使用 FastAPI 支持表单和 json 编码的主体

Sam*_*son 6 python http fastapi

我一直在使用FastAPI来创建基于 HTTP 的 API。它目前支持 JSON 编码的参数,但我也想支持form-urlencoded(甚至理想情况下form-data)同一 URL 上的参数。

按照尼基塔的回答,我可以获得单独的网址:

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"
Run Code Online (Sandbox Code Playgroud)

我可以curl通过执行以下操作来测试这些:

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"
Run Code Online (Sandbox Code Playgroud)

curl -X POST "http://localhost:8000/form" -d 'txt=test'
Run Code Online (Sandbox Code Playgroud)

似乎拥有一个接受两种内容类型并正确解析模型的 URL 会更好。但是上面的代码目前失败了:

{"detail":[{"loc":["body","txt"],"msg":"field required","type":"value_error.missing"}]}
Run Code Online (Sandbox Code Playgroud)

或者

{"detail":"There was an error parsing the body"}
Run Code Online (Sandbox Code Playgroud)

如果我发布到“错误”端点,例如发布到/json.

奖励积分;我也想支持form-data编码参数,因为它似乎相关(我txt在实践中可能会变得很长),但如果它足够不同,可能需要将它变成另一个问题。

Gab*_*lli 6

FastAPI 无法根据内容类型进行路由,您必须在请求中检查并正确解析:

@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())
Run Code Online (Sandbox Code Playgroud)

似乎有一个开放的问题,关于这个功能在GitHub上