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 后端常见场景
200OK请求成功,正常返回结果
201Created创建资源成功,如新建对话
400Bad Request请求参数错误,如缺少必要字段
401Unauthorized未认证,如缺少或错误的 API Key
403Forbidden无权限,如额度用尽或角色不足
404Not Found资源不存在,如对话 ID 无效
429Too Many Requests触发速率限制
500Internal Server Error服务端内部错误
502Bad Gateway上游服务(如模型 API)返回错误
503Service Unavailable服务暂时不可用

429 在 AI 后端中特别常见。模型 API 通常有严格的速率限制,你的后端需要处理这个状态码,做重试或排队。

2.3 Headers 的分类

Headers 按用途可以分成几类:

  • 通用 HeadersDateConnection 等,请求和响应都可以带
  • 请求 HeadersAuthorizationUser-AgentAccept 等,描述客户端的期望
  • 响应 HeadersServerSet-CookieAccess-Control-Allow-Origin 等,描述服务端的处理结果
  • 实体 HeadersContent-TypeContent-LengthContent-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 为什么这样设计,以及你在实际项目中应该如何使用。

延伸阅读

总结

HTTP 协议是浏览器、后端服务、模型 API 之间传递数据的统一格式。一个 AI 请求从发出到返回,每一步都建立在 HTTP 的请求-响应模型上。

本篇覆盖了 HTTP 的核心结构:请求行、响应状态码、Headers、Body,以及方法语义、URI 设计、版本演进和 HTTPS 基础。这些内容构成了后续章节的地基——无论是 Hono 的路由和中间件,还是 AI API 的流式响应和错误处理,都直接建立在这些概念之上。

下一篇会深入 HTTP Headers 和 Body 的细节,包括 Content-Type 协商、认证 Headers、流式 Body 的处理方式。这些内容会直接对接 AI 后端的实际需求。