这是我的 Pydantic 模型:
class Base(BaseModel):
name: str
point: Optional[float] = None
is_accepted: Optional[bool] = False
这是终点:
def create_base(
base: Base = Form(...),
file: List[UploadFile] = File(...)
):
...
我尝试通过多部分表单发送请求,但收到错误:
{
"detail": [
{
"loc": [
"body",
"base"
],
"msg": "value is not a valid dict",
"type": "type_error.dict"
}
]
}
这是我的请求的有效负载:
{
"name": "string",
"point": 10.0,
"is_accepted": true
}
我做错了什么?
请查看此答案了解更多详细信息和选项。
根据 FastAPI 文档,
您可以在路径操作中声明多个
参数,但是 您 也不能将您期望收到的Form
字段声明为Body
,如 该请求将使用以下方式对正文进行编码JSON
代替application/x-www-form-urlencoded
(当表单包含文件时,编码为application/json
)。multipart/form-data
这不是 FastAPI 的限制,它是
协议的一部分。HTTP
因此,如此处所述,可以使用
File
和Form
同时定义文件和表单字段。下面是一个工作示例:
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
模板/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="file">Choose files to upload</label>
<input type="file" id="files" name="files" multiple>
<input type="submit" value="submit">
</form>
</body>
</html>
您还可以通过 OpenAPI 文档(Swagger UI)http://127.0.0.1:8000/docs 或 Python 请求进行测试,如下所示:
测试.py
import requests
url = 'http://127.0.0.1:8000/submit'
files = [('files', open('test_files/a.txt', 'rb')), ('files', open('test_files/b.txt', 'rb'))]
payload ={"name": "foo", "point": 0.13, "is_accepted": False}
resp = requests.post(url=url, data=payload, files = files)
print(resp.json())
可以使用 Pydantic 模型以及 Dependency 来通知“提交”路由(在下面的情况下)参数化变量
base
取决于 Base
类。请注意,此方法需要 base
数据作为查询(而不是正文)参数(然后使用 .dict()
方法将其转换为等效的 JSON 有效负载),并将文件作为正文中的 multipart/form-data
。
app.py
from fastapi import Form, File, UploadFile, Request, FastAPI, Depends
from typing import List
from fastapi.responses import HTMLResponse
from pydantic import BaseModel
from typing import Optional
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory="templates")
class Base(BaseModel):
name: str
point: Optional[float] = None
is_accepted: Optional[bool] = False
@app.post("/submit")
def submit(base: Base = Depends(), files: List[UploadFile] = File(...)):
received_data= base.dict()
return {"JSON Payload ": received_data, "Uploaded Filenames": [file.filename for file in files]}
@app.get("/", response_class=HTMLResponse)
def main(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
再次,您可以使用下面的模板进行测试:
模板/index.html
<!DOCTYPE html>
<html>
<body>
<form method="post" id="myForm" onclick="transformFormData();" 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="file">Choose files to upload</label>
<input type="file" id="files" name="files" multiple>
<input type="submit" value="submit">
</form>
<script>
function transformFormData(){
var myForm = document.getElementById('myForm');
var qs = new URLSearchParams(new FormData(myForm)).toString();
myForm.action = 'http://127.0.0.1:8000/submit?'+qs;
}
</script>
</body>
</html>
如前所述,您可以使用 Swagger UI,或下面的 Python 请求示例:
测试.py
import requests
url = 'http://127.0.0.1:8000/submit'
files = [('files', open('test_files/a.txt', 'rb')), ('files', open('test_files/b.txt', 'rb'))]
payload ={"name": "foo", "point": 0.13, "is_accepted": False}
resp = requests.post(url=url, params=payload, files=files)
print(resp.json())