跳到内容

自定义响应 - HTML、流、文件、其他

默认情况下,**FastAPI** 将使用 JSONResponse 返回响应。

您可以在 直接返回响应 中看到,您可以通过直接返回 Response 来覆盖它。

但是,如果您直接返回 Response (或任何子类,例如 JSONResponse),数据将不会自动转换 (即使您声明了 response_model),并且文档也不会自动生成 (例如,包括特定“媒体类型”在 HTTP 头 Content-Type 中,作为生成的 OpenAPI 的一部分)。

但是,您也可以在 *路径操作装饰器* 中使用 response_class 参数来声明要使用的 Response (例如任何 Response 子类)。

您从 *路径操作函数* 返回的内容将被放入该 Response 中。

如果该 Response 具有 JSON 媒体类型 (application/json),例如 JSONResponseUJSONResponse 的情况,您返回的数据将自动使用您在 *路径操作装饰器* 中声明的任何 Pydantic response_model 进行转换 (和过滤)。

注意

如果您使用没有媒体类型的响应类,FastAPI 将期望您的响应没有内容,因此它不会在其生成的 OpenAPI 文档中记录响应格式。

使用 ORJSONResponse

例如,如果您在压缩性能,您可以安装并使用 orjson 并将响应设置为 ORJSONResponse

导入要使用的 Response 类 (子类) 并将其声明在 *路径操作装饰器* 中。

对于大型响应,直接返回 Response 比返回字典快得多。

这是因为默认情况下,FastAPI 会检查里面的每个项目,并确保它可以使用与教程中解释的相同的 JSON 兼容编码器 序列化为 JSON。这使您可以返回 **任意对象**,例如数据库模型。

但是,如果您确定要返回的内容是 **可与 JSON 序列化的**,您可以将其直接传递给响应类,并避免 FastAPI 通过将您的返回内容传递给 jsonable_encoder 来传递给响应类所产生的额外开销。

from fastapi import FastAPI
from fastapi.responses import ORJSONResponse

app = FastAPI()


@app.get("/items/", response_class=ORJSONResponse)
async def read_items():
    return ORJSONResponse([{"item_id": "Foo"}])

信息

response_class 参数还将用于定义响应的“媒体类型”。

在这种情况下,HTTP 头 Content-Type 将设置为 application/json

它将在 OpenAPI 中作为这样记录。

提示

ORJSONResponse 仅在 FastAPI 中可用,而不是在 Starlette 中。

HTML 响应

要直接从 **FastAPI** 返回包含 HTML 的响应,请使用 HTMLResponse

  • 导入 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 并返回它,而不是将 HTML 返回到 str 中。

通过返回调用 generate_html_response() 的结果,您实际上已经返回了一个 Response,它将覆盖 FastAPI 的默认行为。

但是,由于您也在 response_class 中传递了 HTMLResponseFastAPI 将知道如何在 OpenAPI 和交互式文档中将其记录为具有 text/html 的 HTML。

可用响应

以下是一些可用的响应。

请记住,您可以使用 Response 返回任何其他内容,甚至创建自定义子类。

"技术细节"

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

FastAPI 提供与 fastapi.responses 相同的 starlette.responses,仅作为开发人员的便利。但大多数可用的响应直接来自 Starlette。

Response

主要的 Response 类,所有其他响应都继承自它。

您可以直接返回它。

它接受以下参数

  • content - 一个 strbytes
  • status_code - 一个 int HTTP 状态码。
  • headers - 一个 dict 字符串。
  • media_type - 一个 str,给出媒体类型。例如 "text/html"

FastAPI(实际上是 Starlette)将自动包含一个 Content-Length 头部。它还将包含一个 Content-Type 头部,基于 media_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 中使用的默认响应,如您上面所读。

ORJSONResponse

使用 orjson 的快速替代 JSON 响应,如您上面所读。

信息

这需要安装 orjson,例如使用 pip install orjson

UJSONResponse

使用 ujson 的替代 JSON 响应。

信息

这需要安装 ujson,例如使用 pip install ujson

警告

ujson 在处理某些边缘情况时,不如 Python 的内置实现那么谨慎。

from fastapi import FastAPI
from fastapi.responses import UJSONResponse

app = FastAPI()


@app.get("/items/", response_class=UJSONResponse)
async def read_items():
    return [{"item_id": "Foo"}]

提示

ORJSONResponse 可能是一个更快的替代方案。

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.fastapi.org.cn")

或者您可以在 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.org.cn"

如果您这样做,那么您可以直接从您的路径操作函数中返回 URL。

在这种情况下,使用的 status_code 将是 RedirectResponse 的默认值,即 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.org.cn/"

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())

StreamingResponse 与类文件对象一起使用

如果您有一个类文件对象(例如,由 open() 返回的对象),您可以创建一个生成器函数来迭代该类文件对象。

这样,您就不必先将它全部读入内存,就可以将该生成器函数传递给 StreamingResponse 并返回它。

这包括许多用于与云存储、视频处理等交互的库。

from fastapi import FastAPI
from fastapi.responses import StreamingResponse

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


@app.get("/")
def main():
    def iterfile():  # (1)
        with open(some_file_path, mode="rb") as file_like:  # (2)
            yield from file_like  # (3)

    return StreamingResponse(iterfile(), media_type="video/mp4")
  1. 这是生成器函数。它是一个“生成器函数”,因为它包含 yield 语句。
  2. 通过使用 with 块,我们确保在生成器函数完成之后关闭类文件对象。因此,在它完成发送响应之后。
  3. 这个 yield from 会告诉函数迭代名为 file_like 的东西。然后,对于迭代的每个部分,将该部分作为来自这个生成器函数(iterfile)的一部分进行 yield。

    因此,它是一个生成器函数,它将“生成”工作内部传递给其他东西。

    通过以这种方式进行,我们可以将其放入 with 块中,并以这种方式确保在完成之后关闭类文件对象。

提示

注意,这里我们使用的是标准的 open(),它不支持 asyncawait,因此我们用普通的 def 声明路径操作。

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,但使用一些在包含的 ORJSONResponse 类中未使用的自定义设置。

假设您希望它返回缩进和格式化的 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 更有效地利用它的方法。😉

默认响应类

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

定义此的參數是 default_response_class

在下面的示例中,FastAPI 将在所有路径操作中默认使用 ORJSONResponse,而不是 JSONResponse

from fastapi import FastAPI
from fastapi.responses import ORJSONResponse

app = FastAPI(default_response_class=ORJSONResponse)


@app.get("/items/")
async def read_items():
    return [{"item_id": "Foo"}]

提示

您仍然可以像以前一样在路径操作中覆盖 response_class

其他文档

您也可以使用 responses 在 OpenAPI 中声明媒体类型和许多其他细节:OpenAPI 中的其他响应.