Swagger UI无法正确处理可选上传文件?技术问题咨询
问题:FastAPI可选文件参数在Swagger UI请求报错,curl却正常
基于FastAPI定义了POST接口/evaluate,包含必填表单参数server_id和可选文件参数csv_file,代码如下:
from fastapi import FastAPI, File, Form, UploadFile from typing import Optional from fastapi.responses import JSONResponse app = FastAPI() @app.post("/evaluate") async def evaluate( server_id: str = Form(...), csv_file: Optional[UploadFile] = File(None) # Optional CSV file ): if csv_file: return JSONResponse(content={ "server_id": server_id, "csv_file_name": csv_file.filename }) else: return JSONResponse(content={ "server_id": server_id, "csv_file_name": "No CSV file provided" })
安装依赖命令:
pip install fastapi uvicorn python-dotenv python-multipart
启动服务命令:
uvicorn main:app --reload
使用curl请求时,无论是否携带文件都能正常响应:
- 不带文件的请求:
$ curl -X 'POST' 'http://127.0.0.1:8000/evaluate' -H 'accept: application/json' -F 'server_id=123'
响应:
{"server_id":"123","csv_file_name":"No CSV file provided"}
- 带文件的请求:
$ curl -X 'POST' 'http://127.0.0.1:8000/evaluate' -H 'accept: application/json' -F 'server_id=123' -F 'csv_file=@nada.csv'
响应:
{"server_id":"123","csv_file_name":"nada.csv"}
但通过Swagger UI发起不带csv文件的请求时,会返回422 Unprocessable Entity错误,响应体如下:
{ "detail": [ { "type": "value_error", "loc": [ "body", "csv_file" ], "msg": "Value error, Expected UploadFile, received: <class 'str'>", "input": "", "ctx": { "error": {} } } ] }
提问:这是代码存在问题,还是Swagger UI本身无法处理这种可选文件场景?
原因与解决方案
原因
不是代码本身的问题,是Swagger UI的行为特性导致的:当你在Swagger UI的表单中不选择文件时,它不会省略csv_file这个参数,而是会发送一个空字符串作为该参数的值。而FastAPI收到空字符串后,无法将其解析为UploadFile类型,因此抛出422类型错误。
而curl请求中如果不指定csv_file参数,FastAPI会正确使用你定义的默认值None,所以能正常响应。
解决方案
你可以通过自定义依赖项来处理Swagger UI发送的空值,将其转换为None,修改后的代码如下:
from fastapi import FastAPI, File, Form, UploadFile, Depends from typing import Optional from fastapi.responses import JSONResponse app = FastAPI() def get_optional_file(file: Optional[UploadFile] = File(None)) -> Optional[UploadFile]: # 处理Swagger UI发送的空文件(filename为空字符串的情况) if file and file.filename == "": return None return file @app.post("/evaluate") async def evaluate( server_id: str = Form(...), csv_file: Optional[UploadFile] = Depends(get_optional_file) ): if csv_file: return JSONResponse(content={ "server_id": server_id, "csv_file_name": csv_file.filename }) else: return JSONResponse(content={ "server_id": server_id, "csv_file_name": "No CSV file provided" })
这样修改后,无论是curl请求还是Swagger UI请求,不带文件时都能正常返回预期结果。
内容的提问来源于stack exchange,提问作者KansaiRobot
相关产品推荐
相关产品推荐

