05-Web Standards是什么
前面四篇文章分别聊了 HTTP 协议、请求与响应报文结构、REST 架构风格,以及 REST API 设计规范。这些都是具体的技术概念,但它们背后有一个共同的根基——Web Standards。
Hono 的官方文档反复强调自己是一个「基于 Web Standards」的框架。这句话听起来抽象,但它直接影响着代码能否在 Node.js、Deno、Bun、Cloudflare Workers 之间无缝切换。
本篇要回答的问题:Web Standards 到底是什么?为什么它对 AI 后端开发者有实际价值?
要点
- Web Standards 不是一个具体技术,而是由多个标准化组织维护的一组规范集合
- WHATWG 维护 HTTP 相关的核心 API(Fetch、URL、Streams 等),TC39 维护 JavaScript 语言规范,W3C 负责 CSS 等视觉相关标准
- 对后端开发者最重要的标准 API 包括 Request/Response、Fetch、Headers、Streams、AbortController
- 浏览器、Node.js、Deno、Bun、Edge Runtime 都在实现同一套 API,这是跨运行时可移植性的基础
- Web Standard API 和 Node.js 原生 API 是两套不同的抽象,Node.js 正在逐步采纳前者
- Hono 的路由、中间件、Context 全部基于标准 Request/Response,这是它能支持多运行时的前提
- Web Standards 不是万能的:文件系统、数据库驱动、进程管理仍然依赖具体运行时
1. Web Standards 不是一个东西,而是一组标准
当你听到「Web Standards」时,不要把它理解成某一个具体的规范或某一份文档。它是对一组由不同组织维护的标准的统称。
Web 平台的技术栈大致可以分成三层:
| 层级 | 职责 | 主要标准化组织 |
|---|---|---|
| 语言层 | 定义 JavaScript 语言本身的语法、类型系统、内置对象 | TC39 / Ecma International |
| 平台层 | 定义 Web API(网络、DOM、存储、流、加密等) | WHATWG |
| 表现层 | 定义 CSS、SVG、WebAudio、WebXR 等 | W3C |
WHATWG(Web Hypertext Application Technology Working Group)
WHATWG 由 Apple、Google、Mozilla、Microsoft 等浏览器厂商在 2004 年创立。目前维护着以下对后端开发者至关重要的规范:
- HTML Living Standard:HTML 元素、DOM 模型
- Fetch Standard:
fetch()、Request、Response、Headers - URL Standard:
URL、URLSearchParams - Streams Standard:
ReadableStream、WritableStream、TransformStream - Encoding Standard:
TextEncoder、TextDecoder - Web Crypto API:
crypto.subtle、crypto.getRandomValues - File API / Blob:
Blob、File、FormData
TC39 / Ecma International
TC39 是 Ecma International 的技术委员会,负责 ECMAScript 规范。JavaScript 的语法、Promise、async/await、Array.prototype.at()、structuredClone() 等语言层面的特性都由 TC39 定义。
TC39 proposals 分为 Stage 0 到 Stage 4,只有到 Stage 4 才会被写入正式规范。
W3C(World Wide Web Consortium)
W3C 负责的标准偏向视觉和交互层:CSS、SVG、WebAudio、WebXR、Web Animations 等。这些与后端开发关系较远,但在 Full-stack 场景(如 SSR 渲染)中偶尔会碰到。
三者的关系
简化来说:
TC39 定义「语言」 → WHATWG 定义「平台 API」 → W3C 定义「表现层」
对后端开发者而言,重点关注 WHATWG 和 TC39 就够了。
2. 对后端开发者最重要的 Web Standard APIs
以下这些 API 都定义在 WHATWG 的规范中。它们在浏览器里诞生,但现在已经被 Node.js(18+)、Deno、Bun、Cloudflare Workers 等运行时实现。
| API | 规范来源 | 后端用途 |
|---|---|---|
Request / Response | Fetch Standard | HTTP 请求与响应的标准表示 |
fetch() | Fetch Standard | 发起 HTTP 请求 |
Headers | Fetch Standard | 操作 HTTP 请求头 / 响应头 |
URL / URLSearchParams | URL Standard | URL 解析与查询参数操作 |
ReadableStream / WritableStream / TransformStream | Streams Standard | 流式数据处理 |
TextEncoder / TextDecoder | Encoding Standard | 字符串与字节流转换 |
AbortController / AbortSignal | DOM Standard | 请求取消与超时控制 |
crypto.subtle / crypto.getRandomValues | Web Crypto API | 加密、签名、哈希 |
Blob / File / FormData | File API / HTML Standard | 文件与表单数据处理 |
这些 API 不是某运行时的私有接口,而是跨运行时通用的标准接口。这是它们和 Node.js 原生 http、fs、stream 模块之间最本质的区别。
3. 为什么这些 API 很重要
在 Node.js 占据服务端 JavaScript 垄断地位的年代,服务端代码天然绑定在 Node.js 的原生 API 上。你写一个 HTTP 服务器,用的是 node:http;你处理文件流,用的是 node:stream;你发起网络请求,用的是 node:https。
这种绑定在只部署到 Node.js 时不是问题。但当运行时生态开始分化——Deno 在 2018 年出现,Bun 在 2022 年出现,Cloudflare Workers 在 2017 年推出 Edge Runtime——问题就出现了。
每个运行时都有自己的 HTTP 抽象:
| 运行时 | HTTP 请求表示 | HTTP 响应表示 |
|---|---|---|
| Node.js | IncomingMessage | ServerResponse |
| Deno | Deno.RequestEvent(早期)/ 标准 Request | 标准 Response |
| Bun | 标准 Request | 标准 Response |
| Cloudflare Workers | 标准 Request | 标准 Response |
| Edge Runtime | 标准 Request | 标准 Response |
Node.js 是唯一还在用自己独立抽象的主流运行时。从 Node.js 18 开始,Node.js 内置了标准 fetch()、Request、Response、Headers,并持续缩小与 Web 标准的差距。
如果你写的代码只依赖 Web Standard API,那么它理论上可以在所有这些运行时中运行,不需要针对每个运行时重写代码。这就是跨运行时可移植性。
4. Web Standards vs 传统 Node.js API
下面是一个具体的对比。同样是读取请求体中的 JSON,两种 API 的写法不同:
// 传统 Node.js API(node:http)
import http from 'node:http'
const server = http.createServer((req, res) => {
let body = ''
req.on('data', (chunk) => {
body += chunk
})
req.on('end', () => {
const data = JSON.parse(body)
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({ received: data }))
})
})
// Web Standard API(Fetch API + Request/Response)
// 这段代码可以在 Node.js 18+、Deno、Bun、Cloudflare Workers 中运行
async function handler(request: Request): Promise<Response> {
const data = await request.json()
return new Response(JSON.stringify({ received: data }), {
headers: { 'Content-Type': 'application/json' }
})
}两段代码做的事情一样,但区别在于:
| 对比维度 | Node.js 原生 API | Web Standard API |
|---|---|---|
| 请求体读取 | 监听 data / end 事件,手动拼接 | await request.json() 一行完成 |
| 响应构造 | 分步:writeHead() + end() | 直接构造 new Response(body, options) |
| Headers 操作 | 对象或数组 | 标准 Headers 类 |
| 跨运行时可移植 | 仅 Node.js | Node.js / Deno / Bun / Workers / Edge |
| 流式处理 | node:stream 模块 | 标准 ReadableStream |
| 请求取消 | 需要自己实现 | AbortController / AbortSignal 标准支持 |
Node.js 原生 API 并不是「不好」——它在 Node.js 运行时里功能完备、性能优秀。但当你需要考虑多运行时部署时,Web Standard API 提供了统一的抽象层。
5. 对 AI 后端的实际影响
Web Standards 对 AI 后端开发的影响不是理论层面的,它直接体现在三个具体场景:
5.1 流式响应的统一接口
LLM 的流式输出是 AI 后端的核心需求。不同的 LLM 提供商(OpenAI、Anthropic、本地模型)返回的流式数据格式各异,但写入 HTTP 响应的接口应该是统一的:
// 不管底层是 OpenAI、Anthropic 还是本地模型
// 向客户端返回流式响应的接口是标准的
async function streamResponse(request: Request): Promise<Response> {
const upstream = await fetch('https://api.llm-provider.com/v1/chat', {
method: 'POST',
body: JSON.stringify({ /* ... */ })
})
// upstream.body 是标准 ReadableStream
// 直接透传给客户端,不需要运行时特定的流转换
return new Response(upstream.body, {
headers: {
'Content-Type': 'text/event-stream',
'Transfer-Encoding': 'chunked'
}
})
}ReadableStream 在 Node.js、Deno、Bun、Edge Runtime 中都有实现。如果你的流式逻辑依赖 ReadableStream 而不是 node:stream,这段代码在多个运行时都能运行。
5.2 Hono 的 Context 基于标准 Request/Response
Hono 的 Context 对象不是凭空设计的,它直接包装了标准的 Request 和 Response:
import { Hono } from 'hono'
const app = new Hono()
app.post('/api/chat', async (c) => {
// c.req 是对标准 Request 的封装
const body = await c.req.json()
// c.res 最终会构造标准 Response
return c.json({ message: 'received', data: body })
})因为 Context 基于标准 API,所以当你从本地 Node.js 开发环境部署到 Cloudflare Workers 或 Deno Deploy 时,路由处理函数的代码不需要改动。
5.3 AI SDK 基于标准 API 设计
Vercel AI SDK、Anthropic SDK、OpenAI SDK 等主流 AI 工具库的接口设计,都在向 Web Standard API 靠拢:
// Anthropic SDK 返回的流式响应可以转换为标准 ReadableStream
const stream = await client.messages.stream({
model: 'claude-opus-4-6',
messages: [{ role: 'user', content: 'Hello' }]
})
// toReadableStream() 返回标准 ReadableStream
// 可以直接传给 new Response() 或 Hono 的 c.body()
return new Response(stream.toReadableStream())标准 API 让不同 SDK 之间的数据流可以无缝衔接。
6. Web Standards 的局限
Web Standards 提供了很好的通用抽象,但它不是万能的。以下是实际开发中会碰到的边界:
6.1 没有标准的能力
有些能力在 Web 平台中不存在标准,必须依赖具体运行时:
| 能力 | Node.js | Deno | Bun |
|---|---|---|---|
| 文件系统读写 | node:fs | Deno.readFile() | Bun.file() |
| 子进程 | node:child_process | Deno.Command | Bun.spawn() |
| 数据库驱动 | pg、mysql2 等原生模块 | postgres.js(部分) | bun:sqlite |
| Node.js 生态包 | 完整支持 | 部分兼容 | 部分兼容 |
6.2 标准 API 的实现成熟度差异
同一个标准 API 在不同运行时中的实现成熟度不同。例如:
ReadableStream在 Node.js 18 之前需要--experimental-fetch标志crypto.subtle在某些 Edge Runtime 中支持的算法子集比 Node.js 少fetch()在 Node.js 18 刚引入时不支持一些边界情况
6.3 生态库的绑定
Node.js 生态积累了大量只依赖 node:* 模块的库。如果你的项目依赖这些库,切换到非 Node.js 运行时会遇到困难。不过这种情况在逐步改善——许多流行库(如 drizzle-orm、zod)已经在向跨运行时兼容的方向演进。
6.4 实用建议
在 AI 后端开发中,一个务实的策略是:
- HTTP 层、流式处理、加密、编码转换:用 Web Standard API
- 文件系统、数据库连接、消息队列:用具体运行时的 API 或库,通过依赖注入隔离
这样可以在获得跨运行时收益的同时,保留对底层能力的完整访问。
7. Hono 如何利用 Web Standards
Hono 的核心设计原则是把 Web Standard API 作为唯一的内部抽象。具体体现在三个层面:
7.1 路由与中间件基于标准 Request
Hono 的路由匹配和中间件执行都基于标准 Request 对象。中间件接收的 c.req.raw 就是标准 Request:
app.use('*', async (c, next) => {
// c.req.raw 是标准 Request 对象
const url = new URL(c.req.raw.url)
console.log(`${c.req.raw.method} ${url.pathname}`)
await next()
})7.2 Context 是标准 Request/Response 的封装
前面提到过,Context 的输入输出都是标准 API。你可以在 Hono 路由处理函数中直接操作标准 Response:
app.get('/stream', (c) => {
const stream = new ReadableStream({
start(controller) {
controller.enqueue(new TextEncoder().encode('data: hello\n\n'))
setTimeout(() => {
controller.enqueue(new TextEncoder().encode('data: world\n\n'))
controller.close()
}, 1000)
}
})
// 直接返回标准 Response,ReadableStream 作为 body
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache'
}
})
})7.3 适配器模式实现多运行时支持
Hono 通过「适配器」把标准 Request/Response 和各个运行时的原生 API 做桥接。核心代码逻辑完全不需要改动,适配器负责翻译:
┌────────────────────────┐
│ Hono 核心 (标准 API) │
│ 路由 / 中间件 / Context │
└────────┬───────────────┘
│
┌──────────────────┼──────────────────┐
▼ ▼ ▼
┌────────────────┐ ┌────────────────┐ ┌────────────────┐
│ @hono/node-server│ │@hono/cloudflare│ │ @hono/bun │
│ (node:http 适配) │ │ Workers 适配 │ │ (Bun.serve 适配)│
└────────────────┘ └────────────────┘ └────────────────┘
这意味着你写的路由处理函数、中间件、校验逻辑在部署到不同平台时不需要改动。改动只发生在最外层的入口文件——而入口文件通常只有几行:
// 部署到 Node.js
import { serve } from '@hono/node-server'
import { app } from './app'
serve({ fetch: app.fetch, port: 3000 })
// 部署到 Cloudflare Workers(wrangler.toml 中配置)
export default app核心应用代码(./app)是同一份。这就是 Web Standards 带来的实际收益。
延伸阅读
- WHATWG 官方规范列表
- Fetch Standard — Request、Response、fetch() 的完整定义
- Streams Standard — ReadableStream、WritableStream、TransformStream
- MDN Web Standards 概览
- Node.js 与 Web Standards 对齐进度 — Node.js 内置的全局 Web API 列表
- Hono 多运行时适配器文档
总结
Web Standards 是由 WHATWG、TC39、W3C 等标准化组织共同维护的一组规范。对后端开发者而言,最核心的是 WHATWG 定义的 Fetch、URL、Streams、Encoding 等 API。
这些 API 的价值不在于它们比 Node.js 原生 API「更好」,而在于它们是多个运行时之间的公共约定。当代码只依赖这些标准 API 时,它在 Node.js、Deno、Bun、Cloudflare Workers 之间切换的成本会显著降低。
Hono 把这套标准 API 作为自己的内部抽象,路由、中间件、Context 全部基于标准 Request/Response。这使得 Hono 可以通过适配器运行在多个平台,而应用代码不需要改动。
下一篇会深入 Fetch API 的工作模型,具体看 fetch() 如何在 Hono 中用于发起请求、处理流式响应和做请求取消。