大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
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.getvsapp.post)。 - 响应被 Cloudflare CDN 缓存了:在 Response 里加
Cache-Control: no-cache,或在 Dashboard 的 Page Rules 里关闭缓存。
面试常问
-
Workers 的
fetchhandler 和 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 处理响应后的异步任务。
延伸
- Workers Fetch Handler 官方文档
- Hono 官方文档
- 下一课:Workers KV,学习边缘键值存储的读写模式与缓存策略。