支持FastAPI同时处理表单和JSON编码的请求体

9

我一直在使用FastAPI创建基于HTTP的API。目前,它支持JSON编码的参数,但我还希望支持在同一URL下的form-urlencoded(最好甚至是form-data)参数。

根据Nikita的回答,我可以通过以下方式使不同的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"

我可以使用curl进行测试,方法如下:

curl -X POST "http://localhost:8000/form" -d 'txt=test'

并且

curl -sS -X POST "http://localhost:8000/json" -H "Content-Type: application/json" -d '{"txt":"test"}'

似乎有一个单一的 URL 能够接受两种内容类型并将模型适当地解析会更好。但是以上代码目前无论使用何种方式都会失败:
{"detail":[{"loc":["body","txt"],"msg":"field required","type":"value_error.missing"}]}

或者

{"detail":"There was an error parsing the body"}

如果我将表单编码发布到错误的端点,例如/json。为了得到额外的加分,我还想支持form-data编码参数,因为它似乎相关(我的txt在实践中可能会变得非常长),但如果它足够不同,可能需要另外提问。

这个回答解决了你的问题吗?如何创建一个可以接受表单或JSON主体的FastAPI端点? - Chris
1个回答

13

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())

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接