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 Standardfetch()RequestResponseHeaders
  • URL StandardURLURLSearchParams
  • Streams StandardReadableStreamWritableStreamTransformStream
  • Encoding StandardTextEncoderTextDecoder
  • Web Crypto APIcrypto.subtlecrypto.getRandomValues
  • File API / BlobBlobFileFormData

TC39 / Ecma International

TC39 是 Ecma International 的技术委员会,负责 ECMAScript 规范。JavaScript 的语法、Promiseasync/awaitArray.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 / ResponseFetch StandardHTTP 请求与响应的标准表示
fetch()Fetch Standard发起 HTTP 请求
HeadersFetch Standard操作 HTTP 请求头 / 响应头
URL / URLSearchParamsURL StandardURL 解析与查询参数操作
ReadableStream / WritableStream / TransformStreamStreams Standard流式数据处理
TextEncoder / TextDecoderEncoding Standard字符串与字节流转换
AbortController / AbortSignalDOM Standard请求取消与超时控制
crypto.subtle / crypto.getRandomValuesWeb Crypto API加密、签名、哈希
Blob / File / FormDataFile API / HTML Standard文件与表单数据处理

这些 API 不是某运行时的私有接口,而是跨运行时通用的标准接口。这是它们和 Node.js 原生 httpfsstream 模块之间最本质的区别。

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.jsIncomingMessageServerResponse
DenoDeno.RequestEvent(早期)/ 标准 Request标准 Response
Bun标准 Request标准 Response
Cloudflare Workers标准 Request标准 Response
Edge Runtime标准 Request标准 Response

Node.js 是唯一还在用自己独立抽象的主流运行时。从 Node.js 18 开始,Node.js 内置了标准 fetch()RequestResponseHeaders,并持续缩小与 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 原生 APIWeb Standard API
请求体读取监听 data / end 事件,手动拼接await request.json() 一行完成
响应构造分步:writeHead() + end()直接构造 new Response(body, options)
Headers 操作对象或数组标准 Headers
跨运行时可移植仅 Node.jsNode.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 对象不是凭空设计的,它直接包装了标准的 RequestResponse

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.jsDenoBun
文件系统读写node:fsDeno.readFile()Bun.file()
子进程node:child_processDeno.CommandBun.spawn()
数据库驱动pgmysql2 等原生模块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-ormzod)已经在向跨运行时兼容的方向演进。

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 带来的实际收益。

延伸阅读

总结

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 中用于发起请求、处理流式响应和做请求取消。