跳转至

自定义响应 - HTML、流、文件等

🌐 由 AI 与人类协作翻译

本翻译由人类引导的 AI 生成。🤝

可能存在误解原意或不够自然等问题。🤖

你可以通过帮助我们更好地引导 AI LLM来改进此翻译。

英文版本

默认情况下,FastAPI 会返回 JSON 响应。

你可以像在 直接返回响应 中那样,直接返回 Response 来重载它。

但如果你直接返回一个 Response(或其任意子类,比如 JSONResponse),返回的数据不会自动转换(即使你声明了 response_model),也不会自动生成文档(例如,在生成的 OpenAPI 中,HTTP 头 Content-Type 里的特定「媒体类型」不会被包含)。

你还可以在 路径操作装饰器 中通过 response_class 参数声明要使用的 Response(例如任意 Response 子类)。

你从 路径操作函数 中返回的内容将被放在该 Response 中。

注意

如果你使用不带有媒体类型的响应类,FastAPI 会认为你的响应没有任何内容,所以不会在生成的 OpenAPI 文档中记录响应格式。

JSON 响应

默认情况下 FastAPI 返回 JSON 响应。

如果你声明了一个响应模型,FastAPI 会使用 Pydantic 将数据序列化为 JSON。

如果你没有声明响应模型,FastAPI 会使用 JSON 兼容编码器 中解释的 jsonable_encoder,并把结果放进一个 JSONResponse

如果你在 response_class 中声明了一个 JSON 媒体类型(application/json)的类(比如 JSONResponse),你返回的数据会使用你在 路径操作装饰器 中声明的任意 Pydantic response_model 自动转换(和过滤)。但数据不会由 Pydantic 序列化为 JSON 字节;而是先用 jsonable_encoder 转换后传给 JSONResponse,由它使用 Python 标准 JSON 库序列化为字节。

JSON 性能

简而言之,如果你想要获得最大性能,请使用响应模型,并且不要在 路径操作装饰器 中声明 response_class

# Code above omitted 👆

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

# Code below omitted 👇
👀 Full file preview
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/")
async def create_item(item: Item) -> Item:
    return item


@app.get("/items/")
async def read_items() -> list[Item]:
    return [
        Item(name="Portal Gun", price=42.0),
        Item(name="Plumbus", price=32.0),
    ]

HTML 响应

使用 HTMLResponse 来从 FastAPI 中直接返回一个 HTML 响应。

  • 导入 HTMLResponse
  • HTMLResponse 作为你的 路径操作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>Some HTML in here</title>
        </head>
        <body>
            <h1>Look ma! HTML!</h1>
        </body>
    </html>
    """

信息

参数 response_class 也会用来定义响应的「媒体类型」。

在这个例子中,HTTP 头的 Content-Type 会被设置成 text/html

并且在 OpenAPI 文档中也会这样记录。

返回一个 Response

正如你在 直接返回响应 中了解到的,你也可以通过直接返回响应在 路径操作 中直接重载响应。

和上面一样的例子,返回一个 HTMLResponse 看起来可能是这样:

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)

警告

路径操作函数 直接返回的 Response 不会被 OpenAPI 的文档记录(比如,Content-Type 不会被文档记录),并且在自动化交互文档中也是不可见的。

信息

当然,实际的 Content-Type 头、状态码等等,将来自于你返回的 Response 对象。

在 OpenAPI 中文档化并重载 Response

如果你想要在函数内重载响应,但是同时在 OpenAPI 中文档化「媒体类型」,你可以使用 response_class 参数并返回一个 Response 对象。

接着 response_class 参数只会被用来文档化 OpenAPI 的 路径操作,你的 Response 用来返回响应。

直接返回 HTMLResponse

比如像这样:

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 对象而不是在 str 中返回 HTML。

通过返回函数 generate_html_response() 的调用结果,你已经返回一个重载 FastAPI 默认行为的 Response 对象。

但如果你在 response_class 中也传入了 HTMLResponseFastAPI 会知道如何在 OpenAPI 和交互式文档中使用 text/html 将其文档化为 HTML:

可用响应

这里有一些可用的响应。

要记得你可以使用 Response 来返回任何其他东西,甚至创建一个自定义的子类。

技术细节

你也可以使用 from starlette.responses import HTMLResponse

FastAPI 提供了同 fastapi.responses 相同的 starlette.responses 只是为了方便开发者。但大多数可用的响应都直接来自 Starlette。

Response

其他全部的响应都继承自主类 Response

你可以直接返回它。

Response 类接受如下参数:

  • content - 一个 str 或者 bytes
  • status_code - 一个 int 类型的 HTTP 状态码。
  • headers - 一个由字符串组成的 dict
  • media_type - 一个给出媒体类型的 str,比如 "text/html"

FastAPI(实际上是 Starlette)将自动包含 Content-Length 的头。它还将包含一个基于 media_type 的 Content-Type 头,并为文本类型附加一个字符集。

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

如上文所述,接受文本或字节并返回 HTML 响应。

PlainTextResponse

接受文本或字节并返回纯文本响应。

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,并直接返回一个具备正确 JSON 媒体类型的响应,而不会使用 JSONResponse 类。

这是获得最佳性能的理想方式。

RedirectResponse

返回 HTTP 重定向。默认情况下使用 307 状态码(临时重定向)。

你可以直接返回一个 RedirectResponse

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 参数:

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"

如果你这么做,那么你可以在 路径操作 函数中直接返回 URL。

在这种情况下,将使用 RedirectResponse 的默认 status_code,即 307


你也可以将 status_code 参数和 response_class 参数结合使用:

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

采用异步生成器或普通生成器/迭代器(带有 yield 的函数),然后流式传输响应主体。

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 时才能被取消。如果没有 await,生成器(带有 yield 的函数)无法被正确取消,即使已请求取消也可能继续运行。

由于这个小示例不需要任何 await 语句,我们添加 await anyio.sleep(0),给事件循环一个处理取消的机会。

对于大型或无限流,这一点更为重要。

提示

与其直接返回 StreamingResponse,更推荐遵循 流式数据 的写法,它更方便并在幕后为你处理取消。

如果你在流式传输 JSON Lines,请参阅教程:流式传输 JSON Lines

FileResponse

异步传输文件作为响应。

与其他响应类型相比,接受不同的参数集进行实例化:

  • path - 要流式传输的文件的文件路径。
  • headers - 任何自定义响应头,传入字典类型。
  • media_type - 给出媒体类型的字符串。如果未设置,则文件名或路径将用于推断媒体类型。
  • filename - 如果给出,它将包含在响应的 Content-Disposition 中。

文件响应将包含适当的 Content-LengthLast-ModifiedETag 响应头。

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 参数:

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

在这种情况下,你可以在 路径操作 函数中直接返回文件路径。

自定义响应类

你可以创建你自己的自定义响应类,继承自 Response 并使用它。

例如,假设你想用一些设置来使用 orjson

假设你想让它返回带缩进、格式化的 JSON,因此你想使用 orjson 选项 orjson.OPT_INDENT_2

你可以创建一个 CustomORJSONResponse。你需要做的主要事情是实现一个 Response.render(content) 方法,并返回 bytes

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"}

现在,不再是返回:

{"message": "Hello World"}

...这个响应将返回:

{
  "message": "Hello World"
}

当然,你很可能会找到比格式化 JSON 更好的方式来利用这一点。😉

orjson 或响应模型

如果你追求的是性能,使用响应模型 往往比返回 orjson 响应更好。

使用响应模型时,FastAPI 会使用 Pydantic 直接把数据序列化为 JSON,不需要诸如通过 jsonable_encoder 转换这样的中间步骤(其他情况下会发生)。

并且在底层,Pydantic 使用与 orjson 相同的 Rust 机制来序列化 JSON,所以使用响应模型你已经可以获得最佳性能。

默认响应类

在创建 FastAPI 类实例或 APIRouter 时,你可以指定默认要使用的响应类。

用于定义它的参数是 default_response_class

在下面的示例中,FastAPI 会在所有 路径操作 中默认使用 HTMLResponse,而不是 JSON。

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>"

提示

你仍然可以像之前一样在 路径操作 中重载 response_class

额外文档

你还可以使用 responses 在 OpenAPI 中声明媒体类型和许多其他详细信息:OpenAPI 中的额外响应