响应报文
前言
FastAPI 默认返回 JSON,也支持 HTML、纯文本、重定向、文件流、字节流等其它响应类型。本章整理响应状态码、response_model 以及各类 Response 的用法。
指定响应状态码
from fastapi import status
@app.post("/status_code", status_code=status.HTTP_200_OK)
def status_attribute():
return {"status_code": status.HTTP_200_OK}
使用 response_model 定义响应内容
response_model 用于声明接口的响应结构,FastAPI 会据此过滤、校验并生成 OpenAPI 文档。
from pydantic import BaseModel
class ReqBody1(BaseModel):
username: str
password: str
age: int
address: str | None = None
class RespBody1(BaseModel):
username: str
age: int
address: str | None = None
@router.post("/resptest1", summary="响应模型测试1", response_model=RespBody1)
def resptest1(req: ReqBody1):
return req
# 如果只是去掉几个字段,用 response_model_exclude 更方便,不需要额外定义类
@router.post(
"/resptest2",
response_model=ReqBody1,
response_model_exclude={"password"},
)
def resptest2(req: ReqBody1):
return req
测试响应:
curl -X 'POST' \
'http://127.0.0.1:8000/jsonbody/resptest1' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"username": "string",
"password": "string",
"age": 0,
"address": "string"
}'
# {"username":"string","age":0,"address":"string"}
其它相关参数(完整列表见 定义路由):
response_model_include={"a", "b"}:只返回指定字段。response_model_exclude_unset=True:不返回未赋值的字段。response_model_exclude_defaults=True:不返回使用了默认值的字段。response_model_exclude_none=True:不返回值为None的字段。
Response 类型
Response 是所有响应类的基类,常见子类:
JSONResponse、ORJSONResponse:JSON 格式(ORJSONResponse基于 orjson,性能更好,需安装orjson)。HTMLResponse:HTML 文本。PlainTextResponse:纯文本。RedirectResponse:重定向。StreamingResponse:字节流/文本流(SSE、视频流等)。FileResponse:文件下载。
直接返回字典时,FastAPI 默认使用
JSONResponse序列化。
JSONResponse
from fastapi.responses import JSONResponse
@app.post("/")
async def index():
return JSONResponse(
status_code=404, content={"code": 0, "msg": "ok", "data": None}
)
HTMLResponse
from fastapi.responses import HTMLResponse
@app.get("/html", response_class=HTMLResponse)
async def html_page():
return """
<html>
<head><title>FastAPI</title></head>
<body><h1>Hello HTML</h1></body>
</html>
"""
PlainTextResponse
from fastapi.responses import PlainTextResponse
@app.get("/resptext", summary="字符串响应测试")
def resptext():
return PlainTextResponse(status_code=200, content="hello world")
RedirectResponse
from fastapi.responses import RedirectResponse
@router.get("/respredirect1", summary="内部路由重定向")
def respredirect1():
return RedirectResponse("/resptext", status_code=302)
@router.get("/respredirect2", summary="外部重定向")
def respredirect2():
return RedirectResponse("https://baidu.com", status_code=302)
- 301:永久重定向
- 302:临时重定向
StreamingResponse
流式响应适合逐块输出内容,如 SSE、大文件、视频流。示例:模拟文本流式输出:
import asyncio
from fastapi.responses import StreamingResponse
async def text_stream():
for i in range(5):
yield f"chunk {i}\n"
await asyncio.sleep(0.5)
@router.get("/stream")
async def stream_text():
return StreamingResponse(text_stream(), media_type="text/plain")
提示
SSE 场景一般使用 media_type="text/event-stream",完整示例见 补充:用 FastAPI 和 Streamlit 实现 ChatBot。
FileResponse 与 Excel 下载
FileResponse 一般用于文件下载:
from fastapi.responses import FileResponse
@router.get("/respdownload", summary="下载测试")
async def respdownload():
return FileResponse(path="./test.jpg", filename="test.jpg")
生成并下载 Excel 文件(需安装 openpyxl):
python -m pip install openpyxl
from datetime import datetime
from io import BytesIO
from fastapi import APIRouter
from fastapi.responses import StreamingResponse
from openpyxl import Workbook
router = APIRouter(prefix="/excel", tags=["excel"])
@router.get("/download")
def download_excel():
wb = Workbook()
ws = wb.active
ws.title = "Sheet1"
ws.append(["姓名", "年龄", "城市"])
ws.append(["张三", 18, "北京"])
ws.append(["李四", 22, "上海"])
buf = BytesIO()
wb.save(buf)
buf.seek(0)
filename = f"users_{datetime.now().strftime('%Y%m%d%H%M%S')}.xlsx"
return StreamingResponse(
buf,
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
)
提示
用 BytesIO 直接在内存中生成文件,避免把临时文件写到磁盘;Content-Disposition 指定下载文件名。文件较大时可用 FileResponse(path=...) 指向已生成的文件,并配合 background=BackgroundTask(os.remove, path) 在响应后清理临时文件(见 后台任务)。