HTTP协议基础
要点
- HTTP 是浏览器、后端服务、模型 API 之间传递数据的统一格式,理解它的结构才能定位 AI 后端的真实问题
- 一个 AI 请求的生命周期里,HTTP 负责请求发出、流式数据回传、错误传递这些关键环节
- 请求-响应模型的结构(请求行、Headers、Body)是后续所有接口设计和排错的基础
- 方法语义、幂等性、URI 设计这些概念,直接影响 AI API 的可用性和可维护性
- HTTP 版本演进(1.1 → 2 → 3)对 AI 流式响应(SSE)有直接影响,选型时不能忽略
- HTTPS/TLS 是 AI API 的安全底线,因为传输内容涉及用户对话和密钥
- Hono 的 API 直接映射 HTTP 语义,理解 HTTP 基础能帮助你更快上手后续章节
1. 一个 AI 请求的 HTTP 旅程
我们已经明确了一件事:AI 功能从前端开始,但要交给真实用户使用,后端能力很快会变成瓶颈。
这个瓶颈具体发生在哪里?大部分时候,发生在 HTTP 这一层。
先看一个典型场景:用户在聊天界面输入一句话,点击发送,等待 AI 回复。这个过程中,数据经历了这样一条路径:
浏览器 (前端)
↓ HTTP POST /api/chat
Hono 后端服务
↓ HTTP POST https://api.openai.com/v1/chat/completions
模型 API (OpenAI / 本地模型)
↓ HTTP 200 + SSE 流式数据
Hono 后端服务
↓ HTTP 200 + SSE 流式数据
浏览器 (前端)整个过程中,每一次数据传输都遵循 HTTP 协议。
前端发出请求时,需要指定方法、路径、Headers、Body。后端转发给模型 API 时,也是同样的结构。模型 API 返回流式数据时,用的是 HTTP 的 SSE(Server-Sent Events)机制。最终后端把结果送回前端,走的还是 HTTP。
如果 HTTP 协议的结构不清楚,你遇到的问题会很难定位。
比如:
- 请求发出去了,但模型 API 返回 401,你分不清是 Header 写错了还是 Token 过期了
- 流式响应中途断了,你不知道是连接超时、Body 格式错误,还是 HTTP 版本不兼容
- 前端收到了响应,但解析不到内容,问题可能在 Content-Type 或 Transfer-Encoding
这些问题都发生在 HTTP 层面。理解 HTTP 协议的基础结构,是排查这些问题的前提。
2. 请求-响应模型的基本结构
HTTP 协议的核心是一个请求-响应模型:客户端发请求,服务端返回响应。两边的结构都有固定格式。
2.1 请求的三个部分
一个 HTTP 请求由三部分组成:请求行、Headers、Body。
用一段伪代码表示:
POST /api/chat HTTP/1.1 ← 请求行
Host: example.com ← Headers 开始
Content-Type: application/json
Authorization: Bearer sk-xxx
{"message": "你好"} ← Body请求行包含三个信息:
- 方法(POST):告诉服务端这次请求要做什么
- URI(/api/chat):标识请求的目标资源
- 协议版本(HTTP/1.1):声明使用的 HTTP 版本
Headers 是键值对,携带关于请求的元信息:
Content-Type告诉服务端 Body 的格式Authorization携带身份凭证Host指定目标主机
Body 是请求的实际数据。在 AI 场景中,通常是 JSON 格式的消息内容、模型参数等。
2.2 响应的三个部分
服务端的响应结构类似:状态行、Headers、Body。
HTTP/1.1 200 OK ← 状态行
Content-Type: application/json
{"reply": "你好!有什么可以帮你的?"} ← Body状态行包含:
- 协议版本(HTTP/1.1)
- 状态码(200):用数字表示请求结果
- 原因短语(OK):状态码的简短文字描述
常见状态码在 AI 后端中的含义:
| 状态码 | 含义 | AI 后端常见场景 |
|---|---|---|
| 200 | OK | 请求成功,正常返回结果 |
| 201 | Created | 创建资源成功,如新建对话 |
| 400 | Bad Request | 请求参数错误,如缺少必要字段 |
| 401 | Unauthorized | 未认证,如缺少或错误的 API Key |
| 403 | Forbidden | 无权限,如额度用尽或角色不足 |
| 404 | Not Found | 资源不存在,如对话 ID 无效 |
| 429 | Too Many Requests | 触发速率限制 |
| 500 | Internal Server Error | 服务端内部错误 |
| 502 | Bad Gateway | 上游服务(如模型 API)返回错误 |
| 503 | Service Unavailable | 服务暂时不可用 |
429 在 AI 后端中特别常见。模型 API 通常有严格的速率限制,你的后端需要处理这个状态码,做重试或排队。
2.3 Headers 的分类
Headers 按用途可以分成几类:
- 通用 Headers:
Date、Connection等,请求和响应都可以带 - 请求 Headers:
Authorization、User-Agent、Accept等,描述客户端的期望 - 响应 Headers:
Server、Set-Cookie、Access-Control-Allow-Origin等,描述服务端的处理结果 - 实体 Headers:
Content-Type、Content-Length、Content-Encoding等,描述 Body 的格式
在 AI 后端中,你会频繁接触这几类 Headers:
// Hono 中设置响应 Headers
app.post('/api/chat', async (c) => {
// 实体 Headers
c.header('Content-Type', 'text/event-stream')
c.header('Cache-Control', 'no-cache')
c.header('Connection', 'keep-alive')
// 自定义 Headers
c.header('X-Request-Id', crypto.randomUUID())
c.header('X-Token-Usage', JSON.stringify({ prompt: 100, completion: 50 }))
return c.body(stream, 200)
})关于 Headers 的更多细节(自定义 Headers 的设计、安全相关的 Headers 等),会在后续的中间件和安全章节展开。
3. HTTP 方法语义与 AI 后端
HTTP 协议定义了一组方法,每个方法有明确的语义。在 AI 后端中,方法的选择直接影响 API 的可理解性和可缓存性。
3.1 五个常用方法的语义
| 方法 | 语义 | 安全性 | 幂等性 | AI 后端典型用途 |
|---|---|---|---|---|
| GET | 获取资源 | 是 | 是 | 获取对话历史、获取模型列表 |
| POST | 提交数据/创建资源 | 否 | 否 | 发送聊天消息、创建对话、调用模型 |
| PUT | 全量替换资源 | 否 | 是 | 更新对话标题(整体替换) |
| PATCH | 部分修改资源 | 否 | 否 | 修改对话的某个属性 |
| DELETE | 删除资源 | 否 | 是 | 删除对话、删除消息 |
两个关键概念:
安全性:方法是否只读,不修改服务端状态。GET 是安全的,POST 不是。
幂等性:同一个请求执行多次,结果是否和执行一次相同。GET、PUT、DELETE 是幂等的。POST 和 PATCH 不是。
幂等性在实际场景中的意义:如果网络超时,客户端不确定服务端是否处理了请求。对于幂等方法(GET/PUT/DELETE),可以安全地重试。对于非幂等方法(POST),重试可能导致重复创建。
3.2 为什么 AI 聊天用 POST 而不用 GET
一个常见的疑问:聊天接口为什么用 POST,不用 GET 把消息放在 URL 里?
原因有几个:
Body 可以承载大量数据。 AI 请求的 Body 通常包含完整的对话历史、系统 prompt、模型参数等,数据量可能达到几 KB 甚至更多。URL 有长度限制(通常 2KB-8KB,取决于浏览器和服务端),而 Body 没有这个限制。
POST 语义匹配「创建」操作。 每次聊天实际上是创建了一轮新的对话或消息,POST 的「提交/创建」语义与之匹配。
GET 请求会被缓存。 如果聊天接口用 GET,中间代理或 CDN 可能会缓存响应,导致用户看到别人的回复。POST 请求默认不被缓存。
URL 会被记录到各种日志中。 如果消息内容在 URL 里,它会出现在浏览器历史、服务器日志、代理日志中,带来隐私和安全风险。
// POST:适合 AI 聊天
app.post('/api/chat', async (c) => {
const body = await c.req.json()
// body 可以包含完整的对话历史、模型参数等
return c.json({ reply: '...' })
})
// GET:不适合 AI 聊天
// URL 长度有限,参数会被记录到日志,响应可能被缓存
app.get('/api/chat', (c) => {
const message = c.req.query('message') // 只能传少量参数
// ...
})3.3 流式响应对方法有要求吗
SSE(Server-Sent Events)是 AI 流式响应的主要方式。SSE 对 HTTP 方法没有强制要求,GET 和 POST 都可以用。
但实际中,大部分模型 API(包括 OpenAI)的流式接口用 POST,因为流式请求的 Body 里需要传递参数。你的 Hono 后端转发时,也应该保持 POST 方法。
SSE 的关键在于响应 Headers 和 Body 格式,而不是方法。这部分会在后续「Hono 中间件」和「AI API 服务」章节详细讲解。
4. URI 设计基础
URI 是 HTTP 请求中用来标识资源的部分。在 AI 后端中,URI 的设计直接影响 API 的可理解性和可维护性。
4.1 URI、URL、URN 的区别
这三个概念经常被混用,简单区分一下:
- URI(Uniform Resource Identifier):统一资源标识符,是一个总称,用来唯一标识一个资源
- URL(Uniform Resource Locator):统一资源定位符,是 URI 的一种,描述了资源的位置和访问方式(如
https://example.com/api/chat) - URN(Uniform Resource Name):统一资源名称,是 URI 的另一种,只描述资源的名称,不涉及位置(如
urn:isbn:0451524934)
日常开发中,我们说的「URL」大部分时候其实是 URI。在 HTTP 协议中,请求行里的 URI 通常是相对路径(如 /api/chat),浏览器或 HTTP 客户端会把它和 Host 组合成完整的 URL。
4.2 URI 的组成部分
一个完整的 URI 可以包含多个部分:
https://api.example.com/v1/chat/completions?stream=true#section
\_____/ \_____________/ \_____________________/ \_________/ \______/
协议 主机 路径 查询参数 片段在 AI 后端中,你会频繁接触的是:
- 路径(Path):标识资源层级,如
/v1/chat/completions - 查询参数(Query):传递可选参数,如
?stream=true&model=gpt-4
片段(Fragment,# 后面的部分)只在客户端使用,不会发送到服务端。
4.3 AI API 的 URI 设计模式
主流模型 API 的 URI 设计有一些共同模式:
版本前缀:/v1/、/v2/,用于 API 版本管理。
/v1/chat/completions
/v1/models
/v1/embeddings资源层级:用路径表示资源的从属关系。
/v1/conversations ← 对话列表
/v1/conversations/{id} ← 单个对话
/v1/conversations/{id}/messages ← 某个对话的消息列表查询参数控制行为:用查询参数控制响应的格式或行为。
/v1/chat/completions?stream=true
/v1/models?filter=gpt在 Hono 中,路径参数和查询参数可以很方便地获取:
// 路径参数
app.get('/v1/conversations/:id', (c) => {
const id = c.req.param('id')
// ...
})
// 查询参数
app.get('/v1/models', (c) => {
const filter = c.req.query('filter')
// ...
})URI 设计的更多实践(REST 风格、命名规范、分页参数等)会在「项目结构」和「AI API 服务」章节展开。
5. HTTP 版本演进与 AI 流式响应
HTTP 协议有多个版本,每个版本在性能、特性上有差异。这些差异对 AI 后端,特别是流式响应,有实际影响。
5.1 HTTP/1.1 的局限
HTTP/1.1 是目前使用最广泛的版本,但它有几个已知问题:
队头阻塞(Head-of-Line Blocking):同一个 TCP 连接上,请求必须排队处理。如果前面的请求响应很慢,后面的请求会被阻塞。在 AI 场景中,如果后端同时向模型 API 发起多个请求,队头阻塞会放大延迟。
文本协议开销:HTTP/1.1 是纯文本协议,Headers 没有压缩。每次请求都要重复发送大量 Headers(如 Cookie、Authorization),在高频调用场景下会浪费带宽。
连接管理复杂:HTTP/1.1 默认短连接,每个请求需要建立新的 TCP 连接。虽然可以通过 Connection: keep-alive 复用连接,但浏览器对同一域名的并发连接数有限制(通常 6 个)。
5.2 HTTP/2 的改进
HTTP/2 解决了 HTTP/1.1 的多个问题:
多路复用:同一个 TCP 连接上可以同时传输多个请求和响应,互不阻塞。一个页面可以同时加载几百个资源,不会因为队头阻塞而变慢。
头部压缩(HPACK):HTTP/2 压缩了 Headers,减少了重复传输的开销。
二进制分帧:HTTP/2 把请求和响应拆分成二进制帧,解析效率更高。
服务器推送:服务端可以主动向客户端推送资源(但这个特性在实际中用得不多)。
对 AI 后端来说,HTTP/2 的多路复用特别有用。如果你的后端需要同时调用多个模型 API 或微服务,HTTP/2 可以减少连接数和延迟。
5.3 HTTP/3 与 QUIC
HTTP/3 是最新的版本,基于 QUIC 协议(运行在 UDP 上)。它的核心改进:
解决 TCP 层的队头阻塞:HTTP/2 解决了 HTTP 层的队头阻塞,但底层的 TCP 仍然有这个问题。QUIC 在 UDP 上实现了多路复用,彻底消除了队头阻塞。
快速连接建立:QUIC 把 TLS 握手和连接建立合并,减少了延迟。
连接迁移:QUIC 的连接不依赖 IP 地址,用户在 Wi-Fi 和移动网络之间切换时,连接不会中断。
5.4 对 AI 流式响应的实际影响
AI 流式响应(SSE)的特点是:一个请求建立后,服务端持续推送数据,直到生成完毕。这个过程可能持续几秒到几十秒。
不同 HTTP 版本下 SSE 的表现:
- HTTP/1.1:SSE 可以工作,但因为队头阻塞,如果同一连接上还有其他请求,SSE 的推送可能被延迟。
- HTTP/2:SSE 在多路复用下表现更好,不会因为其他请求而阻塞。但要注意,HTTP/2 的流控制可能影响 SSE 的推送节奏。
- HTTP/3:理论上最适合 SSE,因为底层没有队头阻塞,连接建立也更快。
实际部署时,你需要确认:
- 你的运行时(Node.js、Bun、Cloudflare Workers 等)支持哪个 HTTP 版本
- 你的 CDN 或反向代理是否支持 HTTP/2 或 HTTP/3
- 模型 API 端点使用的 HTTP 版本
这些配置层面的细节会在「部署与运维」章节覆盖。
6. HTTPS 与 TLS 基础
HTTP 是明文传输的,数据在网络中可以被截获和篡改。AI API 传输的内容涉及用户对话、API 密钥、模型参数等敏感数据,必须使用 HTTPS。
6.1 为什么 AI API 必须用 HTTPS
HTTPS 是 HTTP 加上 TLS 加密层。它解决三个问题:
机密性:数据在传输过程中被加密,中间人无法读取内容。如果你的 AI API 用 HTTP,用户的对话内容、API 密钥会以明文形式经过网络中的每一个节点。
完整性:数据在传输过程中不会被篡改。如果响应被中间人修改,客户端能检测到。
身份认证:客户端可以验证服务端的身份,防止被导向假冒的服务器。
主流模型 API(OpenAI、Anthropic、Google 等)全部强制使用 HTTPS。你的后端调用它们时,必须用 https:// 开头的 URL。你的后端暴露给前端的接口,也应该使用 HTTPS。
6.2 TLS 握手的基本流程
HTTPS 的连接建立需要先完成 TLS 握手,然后再传输 HTTP 数据。TLS 1.3(目前最常用版本)的握手流程:
客户端 服务端
| |
| --- ClientHello (支持的加密套件) ---> |
| |
| <--- ServerHello (选择的加密套件) ---- |
| <--- 服务端证书 --------------------- |
| |
| [客户端验证证书] |
| [客户端生成预主密钥,用服务端公钥加密] |
| |
| --- 加密的预主密钥 -----------------> |
| |
| [双方根据预主密钥生成会话密钥] |
| |
| --- Finished (加密) ----------------> |
| <--- Finished (加密) ----------------- |
| |
| [开始传输加密的 HTTP 数据] |TLS 1.3 相比 1.2 减少了往返次数(1-RTT),连接建立更快。
6.3 证书和信任链
TLS 握手时,服务端会发送自己的证书。证书包含:
- 服务端的公钥
- 服务端的域名
- 签发机构(CA)的签名
客户端会验证证书是否可信:
- 证书是否由受信任的 CA 签发
- 证书是否过期
- 证书中的域名是否匹配
如果证书验证失败,浏览器会显示「连接不安全」的警告。
对于自己的后端服务,你需要从受信任的 CA 获取证书。Let's Encrypt 提供免费的证书,Cloudflare 等 CDN 也会自动处理证书。
7. 从 HTTP 到 Hono
讲完 HTTP 基础,回到 Hono。
Hono 的设计哲学是「贴近 Web Standards」。它的 API 直接映射 HTTP 协议的概念:
import { Hono } from 'hono'
const app = new Hono()
// HTTP 方法 → app 的方法
// URI 路径 → 路由字符串
// 请求 → Context 的 req 对象
// 响应 → c.json() / c.body() / c.text() 等
app.post('/api/chat', async (c) => {
// 请求行:POST /api/chat
// Headers:c.req.header('Authorization')
// Body:await c.req.json()
const body = await c.req.json()
// 响应:状态码 200,Content-Type: application/json
return c.json({
reply: `收到消息:${body.message}`,
})
})这种设计的直接好处是:你在 HTTP 协议层面的理解,可以直接迁移到 Hono 的使用中。
- 你知道 POST 适合创建资源,所以聊天接口用
app.post() - 你知道 SSE 需要设置特定的 Headers,所以用
c.header()设置 - 你知道 429 状态码表示速率限制,所以中间件里可以统一处理
- 你知道 HTTPS 是必须的,所以在部署时优先配置 TLS
后续的章节会基于这些 HTTP 基础,逐步展开 Hono 的路由、中间件、RPC、错误处理等能力。每一章都会回到 HTTP 协议的标准,解释 Hono 为什么这样设计,以及你在实际项目中应该如何使用。
延伸阅读
- MDN - HTTP 协议概述:HTTP 协议的完整入门资料,适合系统性学习
- MDN - HTTP 响应状态码:所有 HTTP 状态码的详细说明和用例
- Hono 官方文档 - 路由:Hono 路由的完整 API 参考
- HTTP/2 详解:HTTP/2 的特性、性能影响和最佳实践
- QUIC 协议介绍:HTTP/3 底层协议 QUIC 的通俗解释
总结
HTTP 协议是浏览器、后端服务、模型 API 之间传递数据的统一格式。一个 AI 请求从发出到返回,每一步都建立在 HTTP 的请求-响应模型上。
本篇覆盖了 HTTP 的核心结构:请求行、响应状态码、Headers、Body,以及方法语义、URI 设计、版本演进和 HTTPS 基础。这些内容构成了后续章节的地基——无论是 Hono 的路由和中间件,还是 AI API 的流式响应和错误处理,都直接建立在这些概念之上。
下一篇会深入 HTTP Headers 和 Body 的细节,包括 Content-Type 协商、认证 Headers、流式 Body 的处理方式。这些内容会直接对接 AI 后端的实际需求。