# 自定义 Request 和 APIRoute 类 { #custom-request-and-apiroute-class }
在某些情况下,你可能想要重写 `Request` 和 `APIRoute` 类使用的逻辑。
尤其是,当你本来会把这些逻辑放到中间件里时,这是一个不错的替代方案。
例如,如果你想在应用处理之前读取或操作请求体。
/// danger | 危险
这是一个“高级”特性。
如果你刚开始使用 **FastAPI**,可以先跳过本节。
///
## 使用场景 { #use-cases }
一些使用场景包括:
* 将非 JSON 的请求体转换为 JSON(例如 `msgpack`)。
* 解压缩使用 gzip 压缩的请求体。
* 自动记录所有请求体日志。
## 处理自定义请求体编码 { #handling-custom-request-body-encodings }
来看如何用自定义的 `Request` 子类来解压 gzip 请求。
以及一个 `APIRoute` 子类来使用该自定义请求类。
### 创建自定义 `GzipRequest` 类 { #create-a-custom-gziprequest-class }
/// tip | 提示
这是一个演示工作原理的示例。如果你需要 Gzip 支持,可以直接使用提供的 [`GzipMiddleware`](../advanced/middleware.md#gzipmiddleware){.internal-link target=_blank}。
///
首先,我们创建一个 `GzipRequest` 类,它会重写 `Request.body()` 方法:当请求头中存在相应标记时对请求体进行解压。
如果请求头中没有 `gzip`,则不会尝试解压。
这样,同一个路由类即可同时处理 gzip 压缩和未压缩的请求。
{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[9:16] *}
### 创建自定义 `GzipRoute` 类 { #create-a-custom-gziproute-class }
接着,我们创建 `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` 实例需要这两样:`scope` 和 `receive`。
想了解更多关于 `Request` 的信息,请查看 Starlette 的 Request 文档。
///
由 `GzipRequest.get_route_handler` 返回的函数唯一不同之处是把 `Request` 转换为 `GzipRequest`。
这样,在传给我们的路径操作之前,`GzipRequest` 会(在需要时)负责解压数据。
之后,其余处理逻辑完全相同。
但由于我们修改了 `GzipRequest.body`,在 **FastAPI** 需要读取时,请求体会被自动解压。
## 在异常处理器中访问请求体 { #accessing-the-request-body-in-an-exception-handler }
/// tip | 提示
要解决类似问题,使用 `RequestValidationError` 的自定义处理器中的 `body` 往往更简单([处理错误](../tutorial/handling-errors.md#use-the-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` 类 { #custom-apiroute-class-in-a-router }
你也可以设置 `APIRouter` 的 `route_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] *}