Cloudflare Workers 请求与响应
这一篇深入 Workers 的核心编程模型。掌握了 Request 和 Response,你就掌握了 Workers 的骨架,后面所有功能都是在这个骨架上生长出来的。
入口函数
Workers 的代码统一从 fetch 函数进入
export default {
async fetch(request, env, ctx) {
return new Response("Hello");
},
};
每次有 HTTP 请求进来,平台就会调用一次 fetch 函数,传入三个参数
| 参数 | 作用 |
|---|---|
request |
请求对象,只读,描述”用户想要什么” |
env |
环境对象,读取环境变量和数据库等绑定资源 |
ctx |
上下文对象,管理后台任务等生命周期 |
如果你看过一些老教程,可能会见到 addEventListener("fetch", ...) 的写法,那是早期的 Service Worker 语法,现在已经不推荐,统一用上面的模块写法。
Request 请求对象
request 保存了用户请求的全部信息,常用属性如下
| 属性 | 说明 |
|---|---|
method |
请求方法,GET、POST、PUT、DELETE 等 |
url |
完整请求地址,含查询参数 |
headers |
请求头,可用 get() 读取单个头 |
body |
请求体(GET 请求通常为空) |
读取请求头的示例
export default {
async fetch(request, env, ctx) {
const userAgent = request.headers.get("User-Agent");
return new Response(`你的浏览器是 ${userAgent}`);
},
};
请求体读取规则
POST、PUT 请求通常会携带请求体,Workers 提供了四种读取方式
// 按文本读取
const text = await request.text();
// 按 JSON 读取(请求体是 JSON 时)
const data = await request.json();
// 按表单读取(请求体是表单时)
const form = await request.formData();
// 按二进制读取
const buffer = await request.arrayBuffer();
有一条重要规则要记住,请求体只能读取一次。第一次调用后内容就会被消费掉,再次读取会报错。如果需要读取两次(比如既要解析 JSON 又要保留原内容转发),先调用 request.clone() 复制一份再分别读取
export default {
async fetch(request, env, ctx) {
const copy = request.clone();
const data = await request.json();
// 用 copy 继续做别的处理
return new Response("ok");
},
};
Response 响应对象
返回响应有三种常见方式
// 方式一,new Response 构造
return new Response("hello", {
status: 200,
headers: {
"Content-Type": "text/plain",
},
});
// 方式二,返回 JSON
return Response.json({ name: "Alice", age: 20 });
// 方式三,重定向
return Response.redirect("https://example.com", 302);
new Response 的第一参数是响应体,第二参数可以设置状态码和响应头。记住几个常用状态码
| 状态码 | 含义 |
|---|---|
| 200 | 成功 |
| 301/302 | 重定向 |
| 404 | 资源不存在 |
| 405 | 请求方法不允许 |
| 500 | 服务器内部错误 |
错误处理
代码运行中出现异常时,平台会返回 500 错误。实际开发中要自己捕获异常并返回合理的响应
export default {
async fetch(request, env, ctx) {
try {
const data = await request.json();
return Response.json({ received: data });
} catch (err) {
return Response.json({ error: "请求体不是合法的 JSON" }, { status: 400 });
}
},
};
捕获异常后手动设置状态码返回给用户,比让平台返回笼统的 500 更友好,也方便前端做针对性处理。
后台任务 ctx.waitUntil
有时候响应已经可以返回,但还有一些收尾工作要做,比如写日志、发统计、清理缓存。这些工作不需要用户等待,可以直接用 ctx.waitUntil 挂到后台执行
export default {
async fetch(request, env, ctx) {
// 先记录日志,不阻塞用户
ctx.waitUntil(logRequest(request.url));
return new Response("ok");
},
};
async function logRequest(url) {
// 模拟写日志,这里耗时不影响用户
await fetch("https://log-service.example.com", {
method: "POST",
body: url,
});
}
ctx.waitUntil 接受一个 Promise,函数返回后平台会等待这些后台任务完成再回收资源。
发起子请求 fetch
Workers 内部可以直接发起 HTTP 请求访问其他服务,包括你自己的其他 API 或第三方接口,这就是子请求
export default {
async fetch(request, env, ctx) {
const resp = await fetch("https://api.github.com/repos/cloudflare/workers-sdk", {
headers: { "User-Agent": "my-worker" },
});
const data = await resp.json();
return Response.json({ stars: data.stargazers_count });
},
};
子请求返回的是一个标准 Response 对象,可以用同样的方式读取内容。注意免费计划每次请求最多发起 50 个子请求,需要并发请求多个接口时建议用 Promise.all 合并,避免串行拖慢速度。
这些基础能力组合起来,已经能实现大多数接口逻辑。下一篇用它做一个带路由的多接口服务。
上一篇 Cloudflare Workers 开发工作流
下一篇 Cloudflare Workers 路由与 API 开发