FastAPI:解决HTTP多部分请求传递字典/JSON数据的422错误
FastAPI同时接收文件与表单JSON参数的422错误解决
问题场景
客户端请求代码(requests库)
import requests files = {'template': open('template.xlsx', 'rb')} payload = { 'context': { 'OUT': 'csv', 'SHORT': 'short' }, 'filename': 'file.xlsx', 'content_type': 'application/excel' } r = requests.post('http://localhost:8000/render', files=files, data=payload)
服务端代码(FastAPI)
from fastapi import FastAPI, UploadFile, Form from pydantic import Json app = FastAPI() @app.post('/render') def render(template: UploadFile, context: Json = Form(), filename: str = Form(...), content_type: str = Form(...)): # processing return "ok"
错误信息
{"detail":[{"loc":["body","context"],"msg":"Invalid JSON","type":"value_error.json"}]}
错误原因
当使用requests发送包含文件的multipart/form-data请求时,字典类型的context会被自动序列化为带单引号的字符串(如{'OUT': 'csv', 'SHORT': 'short'})。而FastAPI的Json类型参数要求接收标准JSON格式字符串(必须用双引号),因此解析失败,返回422验证错误。
服务端修复方案
方案1:自定义依赖项手动解析
通过自定义依赖函数,在参数进入视图前完成格式转换,兼容单引号的字符串格式:
from fastapi import FastAPI, UploadFile, Form, Depends, HTTPException import json from typing import Dict app = FastAPI() def get_context(context_str: str = Form(...)) -> Dict: try: # 替换单引号为双引号,适配requests传递的格式 normalized_str = context_str.replace("'", "\"") return json.loads(normalized_str) except json.JSONDecodeError: raise HTTPException(status_code=422, detail="Invalid context JSON format") @app.post('/render') def render( template: UploadFile, context: Dict = Depends(get_context), filename: str = Form(...), content_type: str = Form(...) ): # 处理逻辑 return {"status": "ok", "received_context": context}
方案2:使用Pydantic模型做结构化验证与转换
如果需要对context的字段做合法性校验,可以结合Pydantic模型的验证器:
from fastapi import FastAPI, UploadFile, Form, HTTPException from pydantic import BaseModel, field_validator import json app = FastAPI() class ContextModel(BaseModel): OUT: str SHORT: str @field_validator('*', mode='before') def parse_and_validate(cls, value): if isinstance(value, str): try: normalized_str = value.replace("'", "\"") return json.loads(normalized_str) except json.JSONDecodeError: raise ValueError("Invalid JSON format for context") return value @app.post('/render') def render( template: UploadFile, context: ContextModel = Form(...), filename: str = Form(...), content_type: str = Form(...) ): # 处理逻辑 return {"status": "ok", "received_context": context.dict()}
说明
两种方案都能在服务端完成格式转换,无需修改客户端代码。方案2额外提供了字段级别的合法性校验,适合对参数格式有严格要求的场景。
内容的提问来源于stack exchange,提问作者Альберт Александров
相关产品推荐
相关产品推荐

