首页 >后端开发 >Python教程 >如何在 FastAPI POST 请求中接受 JSON 和文件上传?

如何在 FastAPI POST 请求中接受 JSON 和文件上传?

Mary-Kate Olsen
Mary-Kate Olsen原创
2024-12-19 10:35:12482浏览

How to accept both JSON and file uploads in a FastAPI POST request?

如何在 FastAPI POST 请求中添加文件和 JSON 正文?

具体来说,我希望以下示例能够工作:

from typing import List
from pydantic import BaseModel
from fastapi import FastAPI, UploadFile, File


app = FastAPI()


class DataConfiguration(BaseModel):
    textColumnNames: List[str]
    idColumn: str


@app.post("/data")
async def data(dataConfiguration: DataConfiguration,
               csvFile: UploadFile = File(...)):
    pass
    # read requested id and text columns from csvFile

如果这不是 POST 请求的正确方法,请告诉我如何从上传的 CSV 文件中选择所需的列FastAPI。

根据 FastAPI 文档:

您可以在路径操作中声明多个 Form 参数,但不能同时声明您希望以 JSON 形式接收的 Body 字段,因为请求的正文将使用 application/x-www-form-urlencoded 而不是 application/json 进行编码(当表单包含文件时,它会被编码为multipart/form-data)。

这不是 FastAPI 的限制,它是 HTTP 协议的一部分。

请注意,您需要先安装 python-multipart — 如果您还没有这样做,因为上传的文件是作为“表单数据”发送的。例如:

pip install python-multipart

还应该注意的是,在下面的示例中,端点是使用普通 def 定义的,但您也可以使用 async def (根据您的需要)。请查看此答案,了解有关 FastAPI 中 def 与 async def 的更多详细信息。

如果您正在寻找如何上传文件和字典/JSON 数据列表,请看看这个答案,以及这个答案和这个工作示例的答案(主要基于以下一些方法)。

方法1

如此处所述,可以使用文件和表单同时定义文件和表单字段。下面是一个工作示例。如果您有大量参数并希望与端点分开定义它们,请查看此答案,了解如何使用依赖项类或 Pydantic 模型来声明贴花表单字段。

app.py

from fastapi import Form, File, UploadFile, Request, FastAPI
from typing import List
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates

app = FastAPI()
templates = Jinja2Templates(directory="templates")


@app.post("/submit")
def submit(
    name: str = Form(...),
    point: float = Form(...),
    is_accepted: bool = Form(...),
    files: List[UploadFile] = File(...),
):
    return {
        "JSON Payload": {
            "name": name,
            "point": point,
            "is_accepted": is_accepted,
        },
        "Filenames": [file.filename for file in files],
    }


@app.get("/", response_class=HTMLResponse)
def main(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

您可以通过访问下面的模板来测试上面的示例http://127.0.0.1:8000。如果您的模板不包含任何 Jinja 代码,您也可以返回一个简单的 HTMLResponse。

templates/index.html

<!DOCTYPE html>
<html>
   <body>
      <form method="post" action="http://127.0.0.1:8000/submit"  enctype="multipart/form-data">
         name : <input type="text" name="name" value="foo"><br>
         point : <input type="text" name="point" value=0.134><br>
         is_accepted : <input type="text" name="is_accepted" value=True><br>    
         <label for="files">Choose file(s) to upload</label>
         <input type="file">

您也可以测试此示例使用 /docs 处的交互式 OpenAPI/Swagger UI 自动文档,例如 http://127.0.0.1:8000/docs,或使用Python请求,如下图:

test.py

from typing import List
from pydantic import BaseModel
from fastapi import FastAPI, UploadFile, File


app = FastAPI()


class DataConfiguration(BaseModel):
    textColumnNames: List[str]
    idColumn: str


@app.post("/data")
async def data(dataConfiguration: DataConfiguration,
               csvFile: UploadFile = File(...)):
    pass
    # read requested id and text columns from csvFile

方法 2

还可以使用 Pydantic 模型以及依赖项来通知 /submit 端点(在下面的示例中)参数化变量 base 依赖于 Base 类。请注意,此方法需要将基础数据作为查询(而不是主体)参数,然后对其进行验证并转换为 Pydantic 模型(在本例中,即基础模型)。另外,请注意,永远不应该通过查询字符串传递敏感数据,因为这会带来严重的安全风险 - 请查看此答案以获取有关该主题的更多详细信息。

返回 Pydantic 模型实例时(在本例中,这是基础)来自 FastAPI 端点(例如下面的 /submit 端点),它将使用 jsonable_encoder 在幕后自动转换为 JSON 字符串,如本节中详细说明的 回答。但是,如果您希望在端点内自行将模型转换为 JSON 字符串,您可以使用 Pydantic 的 model_dump_json() (在 Pydantic V2 中),例如,base.model_dump_json(),并直接返回自定义响应,正如之前链接的答案中所解释的;因此,避免使用 jsonable_encoder。否则,为了自己将模型转换为字典,您可以使用 Pydantic 的 model_dump() (在 Pydantic V2 中),例如,base.model_dump(),或简单地 dict(base) (请注意,从返回一个 dict 对象一个端点,FastAPI 仍然会在幕后使用 jsonable_encoder,如上面链接的答案中所述)。您还可以查看此答案以获取相关的 Pydantic 方法和文档。

除了使用 Pydantic 模型作为查询参数之外,还可以直接在端点中定义查询参数,如本答案所示,以及这个答案和这个答案。

除了基本查询参数之外,以下 /submit 端点还需要在请求中编码为 multipart/form-data 的文件body.

app.py

pip install python-multipart

同样,您可以使用下面的模板进行测试,这次使用 JavaScript 来修改form 元素,以便将表单数据作为查询参数传递到 URL 而不是表单数据。

templates/index.html

from fastapi import Form, File, UploadFile, Request, FastAPI
from typing import List
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates

app = FastAPI()
templates = Jinja2Templates(directory="templates")


@app.post("/submit")
def submit(
    name: str = Form(...),
    point: float = Form(...),
    is_accepted: bool = Form(...),
    files: List[UploadFile] = File(...),
):
    return {
        "JSON Payload": {
            "name": name,
            "point": point,
            "is_accepted": is_accepted,
        },
        "Filenames": [file.filename for file in files],
    }


@app.get("/", response_class=HTMLResponse)
def main(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

以上是如何在 FastAPI POST 请求中接受 JSON 和文件上传?的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn