> ## Documentation Index
> Fetch the complete documentation index at: https://docs.arupa.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# HTTP

> 在服务中处理动态 HTTP 请求。

HTTP 允许服务处理动态的 HTTP 请求。当响应取决于请求数据、服务状态或应用逻辑时，请使用它。内核负责匹配传入的请求、检查访问权限、将请求转换为服务协议格式，并把服务的响应发回客户端。

如果内核可以原样提供某个文件，请改用 [static 传输](./transport-static)。

## 注册一个 HTTP 传输并绑定路由

注册一个 HTTP 传输——除了 `id` 之外，它不需要任何特定于类型的配置——然后注册一条或多条绑定到它的路由。一条路由包含：

| 属性          | 含义                          |
| ----------- | --------------------------- |
| `transport` | 该 HTTP 传输的 `id`。            |
| `method`    | 要接受的 HTTP 方法。留空表示该模式接受任意方法。 |
| `pattern`   | 用于选择该路由的 URL 路径。            |
| `access`    | 该路由的可选访问策略。                 |

具体的声明语法取决于所用的语言和生成的 SDK。以下是一种与语言无关的形式，展示了其中涉及的信息：

```text theme={null}
transport: api
method:    POST
pattern:   /my-service/items
access:    <可选策略>
```

一个 HTTP 传输可以承载不止一条路由；如何在多个传输之间划分路由由您自行决定。关于两者之间的一般关系，请参阅[路由与传输](./routing)。

路由模式必须以 `/` 开头。查询字符串不属于该模式的一部分。例如，对 `/my-service/items?limit=10` 的请求会匹配模式 `/my-service/items`；服务会将 `limit=10` 作为请求数据接收到。

## 路径匹配

HTTP 路由使用内核共享的路径模式语言。完整的模式和匹配规则请参阅[内核路由](../kernel/route)。

对于 HTTP 传输，需要注意的重要情形是：

* 不带尾部斜杠的模式只匹配一个精确路径。
* 以 `/` 结尾的模式匹配该路径及其子树。

例如：

```text theme={null}
/my-service/items       → 仅匹配 /my-service/items
/my-service/items/      → 匹配 /my-service/items/ 及其下的路径，
                           例如 /my-service/items/42
```

对于 HTTP 路由，根模式 `/` 只匹配 `/` 本身。不支持通配符形式 `/*`，请改用以 `/` 结尾的模式来表示子树。

当多个模式匹配同一个路径时，最长的匹配模式胜出。内核会先选定路径，再选定方法。如果所选模式没有对应请求方法的路由，客户端会收到 `405 Method Not Allowed`。

对于同一模式，指定了具体方法的路由优先于接受任意方法的路由。方法名称的匹配不区分大小写。

## 请求数据

发送给服务的请求，是[协议](./protocol)中描述的 `HTTPRequest` 消息。其字段包括：

| 字段                           | 定义                         |
| ---------------------------- | -------------------------- |
| `route_id` / `route_pattern` | 与该请求匹配的已注册路由及其模式。          |
| `method`                     | HTTP 方法，例如 `GET` 或 `POST`。 |
| `path`                       | URL 路径，不含查询字符串。            |
| `query`                      | 原始查询字符串，不含开头的 `?`。         |
| `headers`                    | 请求头。                       |
| `body`                       | 完整的请求体。                    |
| `remote_addr`                | HTTP 服务器报告的远程地址。           |
| `user`                       | 已认证的用户（如果有）。               |

对于未经身份验证的请求，`user` 字段不存在。

内核会在把请求发给服务之前先缓冲请求体。请求体大小上限为 8 MiB，超过该限制的请求会在服务处理程序运行之前就被拒绝。

该协议将一次请求和一次响应表示为消息，不提供流式的请求体或响应体，因此对于无法容纳在该限制内、或需要流式传输的负载，请使用其他方案，例如 [proxy 传输](./transport-proxy)。

## 返回一个响应

返回[协议](./protocol)中描述的 `HTTPResponse` 消息：

| 字段        | 定义        |
| --------- | --------- |
| `status`  | HTTP 状态码。 |
| `headers` | 响应头。      |
| `body`    | 响应体字节。    |

如果状态码被省略或为零，内核会使用 `200 OK`。当客户端需要了解如何解读响应体时，请设置 `Content-Type` 响应头。内核会将响应头和响应体原样复制到 HTTP 响应中。

如果服务处理程序在返回响应之前失败，内核会向客户端返回 `502 Bad Gateway` 响应。请在服务内部处理预期的应用错误，并返回相应的状态码和响应体。

## 访问控制

内核会在把请求转发给服务之前评估访问权限。请求必须同时满足为该服务配置的服务级策略，以及在该路由上声明的策略。只要任一策略拒绝该请求，处理程序就不会运行。

空的路由策略不会覆盖服务级的限制。适用于每条路由的规则请使用服务级策略，更具体的规则则使用路由策略。关于策略模型，请参阅[访问控制](../kernel/config-access)。

## 路由冲突

HTTP 路由与其他服务的路由共享同一片应用 URL 空间。同一个路径和方法不能被两个不同的服务同时占用。如果两个服务处理的是不同的方法，且都不接受任意方法，那么它们可以使用相同的路径。一个接受任意方法的路由，会与同一路径下所有指定了具体方法的路由发生冲突。

当一个路由与另一个服务拥有的 static 路由声明了相同的路径模式时，两者也会发生冲突——无论方法是什么，static 路由与非 static 路由在同一路径上总是会冲突。请将路由模式限定在清晰的服务命名空间内，以避免冲突。

内核会在服务注册该路由时检查这些冲突。发生冲突的路由不会被连接，但注册批次中的其余部分仍会照常生效，服务进程或 WASM 实例也会继续运行。该服务会被标记为 `degraded`，其他没有冲突的路由仍然可用。解决冲突后重新注册该路由即可恢复。

这与加载包失败、启动运行时失败，或完成身份握手失败是不同的情况——那些失败会阻止服务启动，并使其处于失败状态。完整的冲突规则和批量注册规则请参阅[路由与传输](./routing)。

## HTTP 处理程序检查清单

加载该服务之前，请确认：

* 每条路由的模式都以 `/` 开头；
* 子树使用以 `/` 结尾的模式，且从不使用 `/*`；
* 查询参数是从请求数据中读取的，而不是包含在路由模式中；
* 路由的访问策略与处理程序所保护的数据相匹配；
* 请求体保持在 8 MiB 限制以内；并且
* 响应设置了适合客户端的状态码、内容类型和响应体。

HTTP 是处理动态行为的合适选择。对于不需要处理程序的打包文件，请改用 [static 传输](./transport-static)。
