跳转至

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

FastAPI 默认使用 JSONResponse 返回响应。

正如直接返回响应一章所述,您可以直接返回 Response 覆盖默认响应。

但如果直接返回 ResponseFastAPI 不会自动转换返回数据,也不会自动生成文档(例如,把 HTTP 请求头 Content-Type 中的特定媒体类型添加到 OpenAPI )。

路径操作装饰器可以声明自定义 Response

路径操作函数返回的内容会被放到 Response 里。

如果 Response 包含 JSON 媒体类型(application/json),比如使用 JSONResponseUJSONResponse 时,路径操作装饰器中声明的 Pydantic response_model 会自动转换(和过滤)返回的数据。

笔记

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

使用 ORJSONResponse

例如,如需压榨性能可以使用 orjson,并把响应设置为 ORJSONResponse

导入要使用的 Response 类或它的子类,然后在路径操作装饰器声明响应类。

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 使用 HTMLResponse 直接返回 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)

警告

OpenAPI 不会存档路径操作函数直接返回的 Response(比如,Content-Type),在 API 文档中也看不到。

说明

当然,实际的 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() 函数没有返回 HTML 字符串,而是生成并返回 Response 对象。

generate_html_response() 函数返回的结果已经覆盖了 FastAPI 的默认 Response

因为在 response_class 还传入了 HTMLResponse,因此,FastAPI 知道要在 OpenAPI 和 API 文档中以 text/html 格式将之存档为 HTML。

可用响应

下面列出一些可用的响应。

注意,使用 Response 可以返回任何其他对象,甚至创建自定义子类。

技术细节

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

FastAPIfastapi.responsesstarlette.responses 一样, 只是为了方便开发者,但其实绝大多数可用的响应都直接继承自 Starlette。

Response

Response 主类,所有其他响应都继承自这个类。

可直接返回。

Response 的参数如下:

  • content - strbytes
  • status_code - int 类型的 HTTP 状态码
  • headers - 由字符串组成的字典
  • media_type - 定义媒体类型的字符串,如 "text/html"

FastAPI(其实是 Starlette)自动包含 Content-Length 响应头,还包含基于 media_typeContent-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

如上文所述,ORJSONResponse 是使用 orjson 的快速 JSON 响应。

UJSONResponse

UJSONResponse 是使用 ujson 的 JSON 响应。

警告

在处理某些边缘情况时,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 状态代码(临时重定向)。

from fastapi import FastAPI
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/typer")
async def redirect_typer():
    return RedirectResponse("https://typer.tiangolo.com")

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 的对象。然后没迭代一部分,就使用生成器函数生成对应的内容;

    因此,它是用于在内部把传输生成内容传递给其他对象的生成器函数。

    由此,可以把它放在 with 代码块里,这样就能确保完成之后正常关闭。

提示

注意,此处使用的是不支持 asyncawait 的标准 open(),所以要使用普通函数声明路径操作。

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

本例从路径操作函数中直接返回文件路径。

默认响应类

创建 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

附加文档

您还可以在 OpenAPI 中使用 response 声明媒体类型和更多详细内容:OpenAPI 附加响应