首页 / FastAPI 入门教程 / 自定义响应

FastAPI 入门教程

自定义响应

本教程共 50 篇 · 第 42 篇 · 更新于 2026-08-12 · 约 7 分钟阅读

FastAPIFastAPI 入门教程自定义响应HTMLResponseFileResponseStreamingResponse

本节目标:掌握 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-LengthLast-ModifiedETag 等头。

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 的真实需求。