自定义响应
本教程共 50 篇 · 第 42 篇 · 更新于 2026-08-12 · 约 7 分钟阅读
本节目标:掌握 FastAPI 不只是会返回 JSON,还能返回 HTML、纯文本、文件,以及像流水一样一段段吐出来的流式响应,并学会自己定制响应类型。
FastAPI 默认把你的返回值包成 JSON 响应。这够应付大多数接口。但总有例外:你想直接吐一段网页 HTML、让用户下载文件、或者实时推送数据。这些都要换一种响应类型。这一章把常见的自定义响应讲透。
什么时候需要自定义响应?最典型的有四种。一是你写的就是个简单页面或探针,返回 HTML 或纯文本比 JSON 更自然。二是接口要提供文件下载,得用专门的文件响应带上正确的头和文件名。三是数据量巨大或实时产生,必须边生成边发,不能等全部凑齐。四是你想完全接管字节层面的输出,比如换一种 JSON 编码、输出特殊格式。认清场景,再选下面的响应类型,就不会乱。
42-1 两种改响应方式
改响应有两种路子。第一种是在路径操作装饰器里写 response_class=某个响应类,FastAPI 会把你的返回值装进这个类,顺便在文档里记好媒体类型。第二种是在函数里直接 return 一个响应对象,彻底覆盖默认行为。
区别在于:用 response_class 时,FastAPI 仍会帮你处理数据、生成文档;直接返回响应对象时,数据不会再被自动转换,文档也(默认)不记录它的媒体类型。按需求选。
42-2 返回 HTML 响应
想直接返回 HTML,用 HTMLResponse。既可以声明 response_class,也可以直接返回对象。
方式一,用 response_class:
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.get("/items/", response_class=HTMLResponse)
async def read_items():
return """
<html>
<head><title>Hello</title></head>
<body><h1>Look ma! HTML!</h1></body>
</html>
"""
声明 response_class=HTMLResponse 后,响应头会被设成 text/html,文档里也会标成 HTML。注意这里返回的是一段 HTML 字符串,FastAPI 帮你包好。
方式二,直接返回对象:
@app.get("/items/")
async def read_items():
html_content = "<html><body><h1>Hi</h1></body></html>"
return HTMLResponse(content=html_content, status_code=200)
直接返回 HTMLResponse 时,响应内容由你给的对象决定,Content-Type 也来自这个对象。代价是文档不会自动记录它是 HTML。
Tip想既覆盖响应、又在文档里标好媒体类型?两个一起用:
response_class=HTMLResponse照写,函数里返回HTMLResponse(...)对象即可。response_class 只管文档,返回的对象管实际响应。
42-3 纯文本响应
返回纯文本用 PlainTextResponse,适合接口探针、健康检查之类:
from fastapi import FastAPI
from fastapi.responses import PlainTextResponse
app = FastAPI()
@app.get("/", response_class=PlainTextResponse)
async def main():
return "Hello World"
42-4 返回文件:FileResponse
要提供文件下载,用 FileResponse。它会异步地把文件流式传回去,并自动带上 Content-Length、Last-Modified、ETag 等头。
from fastapi import FastAPI
from fastapi.responses import FileResponse
app = FastAPI()
some_file_path = "large-video-file.mp4"
@app.get("/")
async def main():
return FileResponse(some_file_path)
也可以用 response_class 的写法,让函数直接返回路径字符串。FileResponse 还能接收这些参数:path 文件路径、headers 自定义头、media_type 媒体类型(不写就按文件名推断)、filename 下载时显示的文件名(会写进 Content-Disposition)。
@app.get("/download", response_class=FileResponse)
async def download():
return some_file_path
42-5 流式响应:StreamingResponse
有些场景不适合一次性给完,比如实时推日志、边读边传大文件。这时用 StreamingResponse,它接收一个生成器,一段一段地把数据发出去。
用异步生成器的例子:
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
app = FastAPI()
async def fake_video_streamer():
for i in range(10):
yield b"some fake video bytes"
@app.get("/")
async def main():
return StreamingResponse(fake_video_streamer())
生成器里用 yield 吐出一块数据,客户端就收到一块。适合”边产生边发送”。
对”像文件一样”的对象(比如 open() 打开的文件),也可以包成普通生成器来流式传输:
some_file_path = "large-video-file.mp4"
@app.get("/")
def main():
def iterfile():
with open(some_file_path, mode="rb") as f:
yield from f
return StreamingResponse(iterfile(), media_type="video/mp4")
Note这里因为用的是同步的
open(),路径操作写成def更合适。如果生成器里没有任何await,取消任务时可能停不下来。官方建议加一句await anyio.sleep(0)让事件循环有机会处理取消,尤其在超长或无限流里。
42-6 重定向响应
想让接口跳转到别处,用 RedirectResponse,默认是 307 临时重定向:
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.get("/typer")
async def redirect_typer():
return RedirectResponse("https://typer.tiangolo.com")
它也能当 response_class 用,配合 status_code 指定其他重定向码。
42-7 直接返回最基础的 Response
最底层是 Response 类,其他响应都继承自它。你可以直接返回它,自己完全控制内容和类型:
from fastapi import FastAPI, Response
app = FastAPI()
@app.get("/legacy/")
def get_legacy_data():
data = """<?xml version="1.0"?>
<shampoo>
<Header>Apply shampoo here.</Header>
<Body>Use soap here.</Body>
</shampoo>
"""
return Response(content=data, media_type="application/xml")
Response 收这些参数:content(字符串或字节)、status_code(整数状态码)、headers(头字典)、media_type(媒体类型,如 text/html)。它自动加 Content-Length,并按 media_type 设好 Content-Type。
42-8 自定义 Response 子类
如果你想换一种序列化方式,可以继承 Response 自己写一个类。关键是实现 render(content) 方法,把内容变成 bytes 返回。
比如用 orjson 输出带缩进的漂亮 JSON:
需要先
pip install orjson;它是可选的高性能 JSON 库。
from fastapi import FastAPI
from fastapi.responses import Response
import orjson
app = FastAPI()
class CustomORJSONResponse(Response):
media_type = "application/json"
def render(self, content) -> bytes:
return orjson.dumps(content, option=orjson.OPT_INDENT_2)
@app.get("/items/", response_class=CustomORJSONResponse)
async def read_items():
return {"message": "Hello World"}
这样返回的 JSON 会带两层缩进,更好看。只要重写 render,你就能控制任何字节层面的输出格式。
Tip真要追求极致 JSON 性能,其实不用 orjson,直接用”响应模型(response_model)“更优。FastAPI 会用 Pydantic 直接序列化,底层和 orjson 一样快,还少了中间转换步骤。
42-9 小结
这一章把自定义响应讲全了:
- 默认返回 JSON,想换类型就指定
response_class或直接返回响应对象。 HTMLResponse吐网页,PlainTextResponse吐纯文本,FileResponse做下载。StreamingResponse用生成器一段段吐数据,适合实时和大文件。RedirectResponse做跳转,Response是最底层完全可控的基类。- 继承
Response重写render,能造出任何你想要的响应格式。
到这里,异步、阻塞、后台任务、中间件与跨域、自定义响应这五块进阶知识就讲完了。把它们组合起来,你就能应对绝大多数 Web API 的真实需求。