fastapi/docs/zh/docs/how-to/custom-request-and-route.md

4.2 KiB
Raw Blame History

自定义 Request 和 APIRoute 类

在某些情况下,你可能想要重写 RequestAPIRoute 类使用的逻辑。

尤其是,当你本来会把这些逻辑放到中间件里时,这是一个不错的替代方案。

例如,如果你想在应用处理之前读取或操作请求体。

/// danger | 危险

这是一个“高级”特性。

如果你刚开始使用 FastAPI,可以先跳过本节。

///

使用场景

一些使用场景包括:

  • 将非 JSON 的请求体转换为 JSON例如 msgpack)。
  • 解压缩使用 gzip 压缩的请求体。
  • 自动记录所有请求体日志。

处理自定义请求体编码

来看如何用自定义的 Request 子类来解压 gzip 请求。

以及一个 APIRoute 子类来使用该自定义请求类。

创建自定义 GzipRequest

/// tip | 提示

这是一个演示工作原理的示例。如果你需要 Gzip 支持,可以直接使用提供的 GzipMiddleware{.internal-link target=_blank}。

///

首先,我们创建一个 GzipRequest 类,它会重写 Request.body() 方法:当请求头中存在相应标记时对请求体进行解压。

如果请求头中没有 gzip,则不会尝试解压。

这样,同一个路由类即可同时处理 gzip 压缩和未压缩的请求。

{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[9:16] *}

创建自定义 GzipRoute

接着,我们创建 fastapi.routing.APIRoute 的自定义子类来使用 GzipRequest

这次,我们会重写 APIRoute.get_route_handler() 方法。

该方法返回一个函数,这个函数负责接收请求并返回响应。

这里我们用它把原始请求包装为 GzipRequest

{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[19:27] *}

/// note | 技术细节

Request 拥有 request.scope 属性,它就是一个 Python dict,包含与请求相关的元数据。

Request 还包含 request.receive,它是一个用于“接收”请求体的函数。

scope 字典和 receive 函数都是 ASGI 规范的一部分。

创建一个新的 Request 实例需要这两样:scopereceive

想了解更多关于 Request 的信息,请查看 Starlette 的 Request 文档

///

GzipRequest.get_route_handler 返回的函数唯一不同之处是把 Request 转换为 GzipRequest

这样,在传给我们的路径操作之前,GzipRequest 会(在需要时)负责解压数据。

之后,其余处理逻辑完全相同。

但由于我们修改了 GzipRequest.body,在 FastAPI 需要读取时,请求体会被自动解压。

在异常处理器中访问请求体

/// tip | 提示

要解决类似问题,使用 RequestValidationError 的自定义处理器中的 body 往往更简单(处理错误{.internal-link target=_blank})。

但本示例同样有效,并展示了如何与内部组件交互。

///

我们也可以用相同的方法在异常处理器中访问请求体。

所需仅是在 try/except 块中处理请求:

{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[14,16] *}

如果发生异常,Request 实例仍在作用域内,因此我们可以在处理错误时读取并使用请求体:

{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[17:19] *}

在路由器中自定义 APIRoute

你也可以设置 APIRouterroute_class 参数:

{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[26] *}

在此示例中,router 下的路径操作将使用自定义的 TimedRoute 类,响应中会多一个 X-Response-Time 头,包含生成响应所用的时间:

{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[13:20] *}