首页
/ FastAPI 自定义响应完整指南:HTML、Stream、File 与自定义 Response 类

FastAPI 自定义响应完整指南:HTML、Stream、File 与自定义 Response 类

2026-09-07 09:08:37作者:裘晴惠Vivianne

本篇文章围绕 FastAPI(当前仓库 fastapi)官方文档中的 Custom Response 高级用法展开,系统讲解如何在 JSON 之外返回 HTML、纯文本、重定向、流式数据与文件响应,并实现自定义 Response 子类与全局默认响应类的配置。读完你将掌握 response_class、直接返回 Responsedefault_response_class 三者之间的取舍与正确姿势,以及在获取最高 JSON 性能时为何应优先使用 Response Model

文章以 docs/en/docs/advanced/custom-response.md(以及 docs/hi/ 等多语言版本)为骨架,代码示例取自 docs_src/custom_response 目录下的真实可运行源码,实现依据可回溯至 fastapi/responses.py

FastAPI 默认返回 JSON,如何覆盖?

默认情况下,FastAPIpath operation 会返回 JSON 响应。当你想返回其他格式时,官方提供了两种互补的覆盖方式:

  1. 在函数内部直接返回 Response 对象(或其子类,如 JSONResponse)。详细说明见 Return a Response directly
  2. path operation decorator 中通过 response_class 参数声明希望使用的响应类(可以是任意 Response 子类)。

两种方式的语义差别很重要:

  • 如果直接返回 Response(而不声明 response_class),返回的数据不会自动转换(即使声明了 response_model 也不生效),同时不会自动生成文档(例如不会在 OpenAPI 中记录 HTTP header Content-Type 中的具体 media type)。
  • 如果通过 response_class 声明响应类,那么从 path operation function 返回的内容会被放入该 Response 实例中,交给 FastAPI 统一处理。

注意:如果使用一个不带 media type 的响应类,FastAPI 会认为你的响应不需要包含内容,因此不会在其生成的 OpenAPI 文档中记录 response 的格式。

JSON 响应:从默认行为到性能最优

默认的 JSON 序列化链路

默认返回 JSON 时,FastAPI 的序列化策略取决于你是否声明了返回类型 / response_model

  • 声明了 Response Model:FastAPI 使用 Pydantic 将数据序列化为 JSON。
  • 未声明 response model:FastAPI 使用 JSON Compatible Encoder 中介绍的 jsonable_encoder 先把数据转成 JSON 兼容的 Python 对象,再放进 JSONResponse 中发送。
  • 声明了 JSON media type(application/json)的 response_class(如 JSONResponse:你返回的数据仍会与 decorator 中声明的 Pydantic response_model 做自动转换与过滤;但数据不会由 Pydantic 直接序列化为 JSON bytes,而是先经 jsonable_encoder 转换,再交给 JSONResponse 类,由 Python 标准库中的 JSON 模块序列化为 bytes。

换句话说,response_modelresponse_class 同时存在时,response_class 会接管"编码"这一步,从而多出一次中间转换。

JSON 性能要点

官方文档给出的结论非常直白:想要最大性能,请使用 Response Model,并且不要在 path operation decorator 中额外声明 response_class。示例见 docs_src/response_model/tutorial001_01_py310.py

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: list[str] = []


@app.post("/items/", response_model=Item)
async def create_item(item: Item) -> Item:
    return item

这一性能建议在当前仓库源码中有直接印证:在 fastapi/responses.py 中,UJSONResponseORJSONResponse 已被标记为 deprecated,其弃用说明明确指出:当设置了 return type 或 response model 时,FastAPI 会通过 Pydantic 直接把数据序列化为 JSON bytes,速度更快且不再需要自定义响应类

返回 HTML 响应

返回 HTML 最简单的方式是使用 HTMLResponse

  1. fastapi.responses 导入 HTMLResponse
  2. path operation decorator 中将其传给参数 response_class

示例见 docs_src/custom_response/tutorial002_py310.py

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>Some HTML in here</title>
        </head>
        <body>
            <h1>Look ma! HTML!</h1>
        </body>
    </html>
    """

注意response_class 参数同时被用来定义响应的 media type。在本例中,HTTP header Content-Type 会被设置为 text/html,并作为响应格式记录在 OpenAPI 中(自动文档会展示正确的 media type)。

直接返回一个 Response

Return a Response directly 所述,你也可以在 path operation 内部构造响应并直接返回,从而完全覆盖 FastAPI 的默认行为。上面的例子改写为直接返回 HTMLResponse 如下,见 docs_src/custom_response/tutorial003_py310.py

from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI()


@app.get("/items/")
async def read_items():
    html_content = """
    <html>
        <head>
            <title>Some HTML in here</title>
        </head>
        <body>
            <h1>Look ma! HTML!</h1>
        </body>
    </html>
    """
    return HTMLResponse(content=html_content, status_code=200)

警告:由 path operation function 直接返回的 Response 不会被记录进 OpenAPI(例如 Content-Type 不会出现在文档中),也不会在自动交互式文档中可见。

说明:当然,实际发出的 Content-Type header、status code 等都来自你返回的那个 Response 对象本身。

同时在 OpenAPI 中记录并覆盖 Response

如果你既想从函数内部返回自定义的 Response 对象,又想让接口文档显示正确的 media type,可以把两种方式结合:同时使用 response_class 参数与返回 Response 对象

此时 response_class 只负责 OpenAPI path operation 的文档化,而实际发送的仍是你返回的那个 Response

直接返回 HTMLResponse 的例子

示例见 docs_src/custom_response/tutorial004_py310.py

from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI()


def generate_html_response():
    html_content = """
    <html>
        <head>
            <title>Some HTML in here</title>
        </head>
        <body>
            <h1>Look ma! HTML!</h1>
        </body>
    </html>
    """
    return HTMLResponse(content=html_content, status_code=200)


@app.get("/items/", response_class=HTMLResponse)
async def read_items():
    return generate_html_response()

在本例中,辅助函数 generate_html_response() 直接生成并返回一个 Response(而不是返回 HTML 字符串);read_items() 返回该函数的调用结果,即一个已经构造好的 Response,它会覆盖 FastAPI 的默认行为。与此同时,由于你在 response_class 中传入了 HTMLResponse,FastAPI 知道应把该接口在 OpenAPI 与交互式文档中记录为 media type 为 text/html 的 HTML 响应。

下图即该接口在自动交互式文档中的效果——成功响应(200)的 Content-Type 下拉框已显示为 text/html

FastAPI 交互式文档中 /items/ 接口响应 Content-Type 为 text/html 的截图

可用的响应类清单

以下是常用响应类的概览。需要注意:Response 基类足够通用,你也可以基于它返回任何内容,甚至创建自定义子类。

技术细节:你完全可以写 from starlette.responses import HTMLResponse。FastAPI 只是出于开发者便利,把与 starlette.responses 相同的类以 fastapi.responses 的形式重新导出,绝大多数响应类实际上直接来自 Starlette。这一导出关系见 fastapi/responses.py

Response:所有响应的基类

Response 类,其余所有响应都继承自它,可直接返回。它接受以下参数:

参数 类型与说明
content 一个 strbytes,作为响应体内容
status_code 一个 int 类型的 HTTP 状态码
headers 由字符串组成的 dict,作为自定义响应头
media_type 一个 str,指明 media type,例如 "text/html"

FastAPI(底层是 Starlette)会自动加入 Content-Length header;同时基于 media_type 生成 Content-Type header,并对文本类 media type 追加 charset。

直接返回自定义 media type(例如 XML)的典型用法见 docs_src/response_directly/tutorial002_py310.py

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>
        You'll have to use soap here.
    </Body>
    </shampoo>
    """
    return Response(content=data, media_type="application/xml")

HTMLResponse

接收一段文本或 bytes,返回 HTML 响应,即上文所讲的用法。

PlainTextResponse

接收一段文本或 bytes,返回纯文本响应。示例见 docs_src/custom_response/tutorial005_py310.py

from fastapi import FastAPI
from fastapi.responses import PlainTextResponse

app = FastAPI()


@app.get("/", response_class=PlainTextResponse)
async def main():
    return "Hello World"

JSONResponse

接收任意数据,返回以 application/json 编码的响应。这是 FastAPI 中使用的默认响应(如前述 JSON 部分)。

技术细节:如果你声明了 response model 或 return type,FastAPI 会直接用它们把数据序列化为 JSON,并直接返回一个具有正确 JSON media type 的响应,不再经过 JSONResponse。这是获取最佳性能的理想做法——与文档"JSON Performance"一节及 ORJSONResponse/UJSONResponse 被弃用的原因完全一致。

RedirectResponse

返回 HTTP 重定向。默认使用 307 状态码(Temporary Redirect)。有三种使用方式:

方式一:直接返回 RedirectResponse,见 docs_src/custom_response/tutorial006_py310.py

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 参数中使用,见 docs_src/custom_response/tutorial006b_py310.py

from fastapi import FastAPI
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/fastapi", response_class=RedirectResponse)
async def redirect_fastapi():
    return "https://fastapi.tiangolo.com"

这样做之后,你可以直接从 path operation function 返回 URL 字符串。此时实际使用的 status_codeRedirectResponse 的默认值 307

方式三:status_coderesponse_class 参数组合,见 docs_src/custom_response/tutorial006c_py310.py

from fastapi import FastAPI
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/pydantic", response_class=RedirectResponse, status_code=302)
async def redirect_pydantic():
    return "https://docs.pydantic.dev/"

StreamingResponse

接收一个 async generator 或普通 generator/iterator(即含 yield 的函数),将响应体以流式方式发送。示例见 docs_src/custom_response/tutorial007_py310.py

import anyio
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"
        await anyio.sleep(0)


@app.get("/")
async def main():
    return StreamingResponse(fake_video_streamer())

技术细节:一个 async 任务只有在到达 await 时才能被取消。如果 generator(含 yield 的函数)内部没有任何 await,它无法被正确取消,即使在取消请求发出后仍可能继续运行。上面的小例子本身不需要任何 await 语句,因此特意加入 await anyio.sleep(0),给事件循环一个处理取消的机会。对于大型或无限流,这一点的意义会更加重要。

提示:与其直接返回 StreamingResponse,官方更推荐采用 Stream Data 中的写法——它更便利,并且会在背后帮你处理取消。若你要流式发送 JSON Lines,请参考 Stream JSON Lines 教程。此外,仓库中还提供基于流式传输的 EventSourceResponse(SSE),可从 fastapi/sse.pydocs_src/server_sent_events 中进一步了解。

FileResponse

以异步方式把一个文件作为响应流式返回。它的构造参数与其它响应类型不同:

参数 说明
path 要流式传输的文件的文件路径
headers 以字典形式提供的任意自定义响应头
media_type 指明 media type 的字符串;若未设置,会用 filename 或 path 推断 media type
filename 若设置,会包含在响应的 Content-Disposition

文件响应会自动携带合适的 Content-LengthLast-ModifiedETag header。

直接返回示例见 docs_src/custom_response/tutorial009_py310.py

from fastapi import FastAPI
from fastapi.responses import FileResponse

some_file_path = "large-video-file.mp4"
app = FastAPI()


@app.get("/")
async def main():
    return FileResponse(some_file_path)

也可以使用 response_class 参数,见 docs_src/custom_response/tutorial009b_py310.py

from fastapi import FastAPI
from fastapi.responses import FileResponse

some_file_path = "large-video-file.mp4"
app = FastAPI()


@app.get("/", response_class=FileResponse)
async def main():
    return some_file_path

此时你可以直接从 path operation function 返回文件路径字符串。

自定义响应类(Custom Response Class)

Response 继承即可创建自己的响应类。官方示例以接入 orjson 并输出缩进格式化 JSON 为例,核心工作只有一个:实现 Response.render(content) 方法,让它把内容转成 bytes 返回

完整示例见 docs_src/custom_response/tutorial009c_py310.py

from typing import Any

import orjson
from fastapi import FastAPI, Response

app = FastAPI()


class CustomORJSONResponse(Response):
    media_type = "application/json"

    def render(self, content: Any) -> bytes:
        assert orjson is not None, "orjson must be installed"
        return orjson.dumps(content, option=orjson.OPT_INDENT_2)


@app.get("/", response_class=CustomORJSONResponse)
async def main():
    return {"message": "Hello World"}

于是响应体从紧凑的一行 JSON:

{"message": "Hello World"}

变为经过缩进与格式化的多行 JSON:

{
  "message": "Hello World"
}

需要提醒的是,render() 的自定义绝不仅限于美化 JSON——你可以通过它实现任何自定义的字节级序列化逻辑。

相关事实:当前仓库的 fastapi/responses.py 还保留了内置的 ORJSONResponse(同样继承自 JSONResponse,在 render() 中使用 orjson.OPT_NON_STR_KEYS | orjson.OPT_SERIALIZE_NUMPY),但它目前已被标记为弃用,弃用说明建议改用 response model 由 Pydantic 直接序列化。

orjson 还是 Response Model?

如果你追求的是性能,官方建议使用 Response Model 而非 orjson 自定义响应:

  • 使用 response model 时,FastAPI 会用 Pydantic 直接把数据序列化为 JSON,没有中间步骤(例如不会像其他情况那样先用 jsonable_encoder 转换一次)。
  • Pydantic 底层序列化 JSON 所使用的 Rust 机制与 orjson 相同,因此使用 response model 本身就已经能得到最优性能。

默认响应类(Default Response Class)

在创建 FastAPI 类实例或 APIRouter 时,可以通过参数 default_response_class 指定全局默认使用的响应类。

下面示例中,app 的所有 path operation 默认都会使用 HTMLResponse(而非 JSON),见 docs_src/custom_response/tutorial010_py310.py

from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI(default_response_class=HTMLResponse)


@app.get("/items/")
async def read_items():
    return "<h1>Items</h1><p>This is a list of items.</p>"

提示:全局设置了 default_response_class 后,你依然可以在单个 path operation 中用 response_class 覆盖,优先级为接口级声明高于应用级默认值。同样的机制也适用于通过 APIRouter(default_response_class=...) 为某个路由分组单独设置默认响应类。

补充:在 OpenAPI 中记录更多响应细节

除了 media type,你还可以用 responses 在 OpenAPI 中声明更丰富的响应细节(如不同状态码对应的响应模型、描述等),详见 Additional Responses in OpenAPI

小结

围绕 FastAPI 的 Custom Response 用法,本文覆盖了如下决策路径,可以直接作为实战速查:

目标 推荐做法
返回 HTML / 纯文本 / 重定向 / 文件 在 decorator 中用 response_class 声明对应响应类,让 OpenAPI 正确记录 media type
需要完全自定义响应对象 在函数内直接返回 Response(注意不会写入 OpenAPI 文档)
既要自定义对象又要文档化 同时使用 response_class 与直接返回 Response,两者各司其职
流式响应 优先参考 Stream Data,而不是手动构造 StreamingResponse
最大 JSON 性能 使用 Response Model,不额外声明 response_class
全局统一响应格式 FastAPI(...)APIRouter(...) 中设置 default_response_class
全新响应格式 继承 Response 并实现 render(content) -> bytes

所有代码示例均为仓库内真实源码,可对照 docs_src/custom_responsedocs_src/response_directlydocs_src/response_model 目录,结合 uvicorn 本地运行验证。

登录后查看全文
热门项目推荐
相关项目推荐