Workers 核心
Fetch API 与中间件。
Workers 核心
这一课你将能做到
- 编写完整的 Worker Fetch Handler,处理 GET/POST、JSON、Header、Query 参数
- 用 Hono 或原生 Fetch API 实现路由、中间件、错误处理
- 解释 Workers 的
Request / Response / ExecutionContext 生命周期
为什么要学
Worker 的核心就是一个 fetch(request, env, ctx) 函数。听起来简单,但里面的门道很多:请求体怎么解析?大文件怎么流式传输?怎么同时转发到多个后端?怎么加鉴权中间件?
人话:Workers 核心就是「如何用标准 Web API 优雅地处理 HTTP 请求」。学会了这套,不管是写 API 网关、做请求聚合、还是做边缘鉴权,都游刃有余。
先建立图像
用户请求 ──> [Cloudflare Edge Node]
│
▼
Worker.fetch(request, env, ctx)
│
┌───────────┼───────────┐
▼ ▼ ▼
[读取请求] [业务逻辑] [访问绑定]
│ │ │
▼ ▼ ▼
[返回 Response] [KV/R2/D1/Queue]
│
▼
用户收到响应
ctx(ExecutionContext)是 Workers 特有的:它提供 waitUntil() 和 passThroughOnException(),让你在返回响应后还能继续异步执行(比如写日志、发统计)。
跟着做
步骤 1:最基础的 Fetch Handler
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const url = new URL(request.url);
if (url.pathname === '/') {
return new Response('Hello Edge!');
}
if (url.pathname === '/api/user') {
return Response.json({ id: 1, name: 'Alice' });
}
return new Response('Not Found', { status: 404 });
},
} satisfies ExportedHandler<Env>;
步骤 2:解析 Query、Header、Body
export default {
async fetch(request: Request): Promise<Response> {
const url = new URL(request.url);
const q = url.searchParams.get('q');
const auth = request.headers.get('Authorization');
if (request.method === 'POST') {
const contentType = request.headers.get('content-type') ?? '';
if (contentType.includes('application/json')) {
const body = await request.json();
return Response.json({ received: body });
}
if (contentType.includes('application/x-www-form-urlencoded')) {
const formData = await request.formData();
const name = formData.get('name');
return Response.json({ name });
}
}
return Response.json({ q, auth });
},
} satisfies ExportedHandler<Env>;
步骤 3:用 Hono 实现路由和中间件
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { bearerAuth } from 'hono/bearer-auth';
const app = new Hono<{ Bindings: Env }>();
// CORS 中间件
app.use('/api/*', cors());
// 鉴权中间件
app.use('/api/admin/*', bearerAuth({ token: 'secret-token' }));
app.get('/api/users/:id', (c) => {
const id = c.req.param('id');
return c.json({ id, name: 'Alice' });
});
app.post('/api/users', async (c) => {
const body = await c.req.json();
return c.json({ created: body }, 201);
});
export default app;
步骤 4:请求转发与响应改写
export default {
async fetch(request: Request): Promise<Response> {
const url = new URL(request.url);
// 转发到源站
if (url.pathname.startsWith('/api/')) {
const backend = new URL(url.pathname.replace('/api', ''), 'https://origin.internal');
const modifiedRequest = new Request(backend, request);
modifiedRequest.headers.set('X-Forwarded-By', 'cloudflare-worker');
const response = await fetch(modifiedRequest);
// 改写响应 Header
const newResponse = new Response(response.body, response);
newResponse.headers.set('Cache-Control', 'public, max-age=60');
return newResponse;
}
return new Response('Static asset');
},
} satisfies ExportedHandler<Env>;
步骤 5:使用 ctx.waitUntil 做异步任务
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const response = new Response('Processing...');
// 响应返回后,继续执行异步任务
ctx.waitUntil(
(async () => {
await env.MY_QUEUE.send({ type: 'log', path: new URL(request.url).pathname });
await env.MY_KV.put(`log:${Date.now()}`, request.url);
})()
);
return response;
},
} satisfies ExportedHandler<Env>;
讲清楚要点
- Request 是流式的:
request.body 是一个 ReadableStream,如果只需要 JSON 就直接 await request.json();大文件建议用 request.body.pipeTo() 直接转发到 R2 或另一个后端,避免内存爆掉。
- Response 也是流式的:可以用
new Response(stream) 返回流式数据,配合 TransformStream 做实时压缩、加密、格式转换。
- Hono 是事实标准:虽然原生 Fetch Handler 也能写路由,但 Hono 提供了中间件、参数解析、错误处理、CORS、JWT 等开箱即用的功能,代码更简洁。
ctx.waitUntil 不是万能的:它最多再运行 30 秒(或更久,取决于 plan),适合写日志、发消息,不适合做长时间任务。长时间任务应该用 Queues 或 Cron Triggers。
- 不要依赖全局状态:Worker 实例可能被复用,也可能被销毁。全局变量可能在不同请求间共享,也可能不共享。状态请放到 KV、D1、Durable Objects。
你可能会懵的地方
-
为什么 request.json() 会报错 Unexpected end of JSON input?
请求体为空或格式不对。先检查 content-type 是否真的是 JSON,再用 try-catch 包裹解析逻辑。
-
Response 返回了但 ctx.waitUntil 里的代码没执行?
waitUntil 不保证一定执行,Worker 可能因为超时或平台限制被提前终止。关键业务不要放在 waitUntil 里。
-
怎么在 Hono 里访问绑定的 KV?
通过 c.env.MY_KV,前提是你在 new Hono<{ Bindings: Env }>() 里声明了 Bindings 泛型。
-
Worker 能处理 WebSocket 吗?
可以,但需要用 Durable Objects 来管理连接状态。普通 Worker 只能做 WebSocket 的「升级握手」,真正的长连接消息处理需要 DO。
-
Worker 里能调用外部 API 吗?
可以,用标准 fetch()。注意默认没有超时,建议用 AbortController 设置超时。
错法对照
| 错法 | 问题 | 正解 |
|---|
把大文件 await request.arrayBuffer() 全部读进内存 | 内存超限,Worker 崩溃 | 用 request.body.pipeTo() 流式转发 |
在全局作用域 let cache = {} 存数据 | 不同请求/节点不共享,可能读到脏数据 | 用 KV 或 Durable Objects |
ctx.waitUntil 里做关键业务逻辑 | 可能不执行或超时 | 关键逻辑放在响应前,或用 Queues |
直接 fetch(request) 转发原始请求 | 会带上原始 Host Header,导致源站拒绝 | 创建 new Request(backendUrl, request) 并修改 headers |
用 process.env 读环境变量 | Workers 没有 process | 通过 env 参数或 c.env 访问 |
卡点急救
- POST 请求体解析失败:确认
content-type 正确;如果是 multipart/form-data,用 request.formData() 而不是 request.json()。
- Hono 路由 404:检查路径是否以
/ 开头;检查是否绑定了正确的 HTTP 方法(app.get vs app.post)。
- 响应被 Cloudflare CDN 缓存了:在 Response 里加
Cache-Control: no-cache,或在 Dashboard 的 Page Rules 里关闭缓存。
面试常问
-
Workers 的 fetch handler 和 Express 的 app.get 有什么本质区别?
Workers 的 fetch 是请求驱动的,没有常驻进程;Express 是常驻 Node.js 进程。Workers 的 Request/Response 是 Web 标准 API,Express 的是 Node 特有的 req/res 对象。Workers 天然分布式,Express 需要自己部署多实例。
-
ctx.waitUntil 适合做什么?
适合「响应后异步执行」的非关键任务:写日志、发统计、清理缓存。不适合关键业务逻辑,因为不保证执行完成,且有 30 秒限制。
-
Workers 里怎么实现中间件?
用 Hono 的 app.use(),或者自己封装高阶函数:接收 request,返回 Response 或 null(继续下一个)。核心思想是「洋葱模型」:请求进入时逐层处理,响应返回时逐层处理。
自测三问
Request 和 Response 在 Workers 中是什么类型的对象?它们和 Node.js 的 req/res 有什么区别?
- 什么时候应该用
ctx.waitUntil,什么时候不应该?
- 如果要在 Worker 里同时调用三个外部 API 并合并结果,你会怎么写?(提示:
Promise.all)
小结一句话
Workers 核心就是标准 Web API 的 fetch handler:用 Request/Response/ExecutionContext 处理 HTTP 请求,用 Hono 简化路由和中间件,用 ctx.waitUntil 处理响应后的异步任务。
延伸