Cloudflare Workers 路由与 API 开发
一个 Worker 可以对外提供多个接口,通过 URL 路径区分不同的业务功能。这一篇从 URL 解析讲起,用纯 JavaScript 实现 RESTful 风格的 CRUD 接口,并解决跨域问题。
URL 解析
request.url 是完整地址字符串,直接用正则处理太麻烦,标准做法是转成 URL 对象
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
const path = url.pathname; // /users/123
const query = url.searchParams; // ?page=2 的解析器
return Response.json({ path });
},
};
URL 对象的常用属性
| 属性 | 示例值 | 说明 |
|---|---|---|
pathname |
/users/123 |
路径部分 |
searchParams |
?page=2 的解析结果 |
查询参数解析器 |
host |
my-worker.xxx.workers.dev |
域名部分 |
protocol |
https: |
协议部分 |
searchParams 是 URLSearchParams 对象,用 get() 读取参数
const page = url.searchParams.get("page") || "1";
const keyword = url.searchParams.get("q") || "";
按路径分发路由
最简单的路由方式是用 switch 语句按 pathname 分发
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
switch (url.pathname) {
case "/":
return new Response("首页");
case "/about":
return new Response("关于我们");
case "/api/users":
return handleUsers(request);
default:
return new Response("页面不存在", { status: 404 });
}
},
};
解析动态路径参数
RESTful 接口经常需要在路径中携带 ID,例如 /api/users/123。可以把路径拆开取参数
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
const parts = url.pathname.split("/").filter(Boolean);
// /api/users/123 => ["api", "users", "123"]
if (parts[0] === "api" && parts[1] === "users") {
const id = parts[2];
if (id) {
return Response.json({ id, action: "获取单个用户" });
}
return Response.json({ action: "获取用户列表" });
}
return new Response("页面不存在", { status: 404 });
},
};
RESTful 接口实战
下面用内存数组实现一组完整的 CRUD 接口,覆盖 GET、POST、PUT、DELETE 四种方法
const users = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
];
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
const parts = url.pathname.split("/").filter(Boolean);
// 只处理 /api/users 开头的请求
if (!(parts[0] === "api" && parts[1] === "users")) {
return new Response("页面不存在", { status: 404 });
}
const id = parts[2] ? Number(parts[2]) : null;
const method = request.method;
// 获取单个用户
if (method === "GET" && id) {
const user = users.find((u) => u.id === id);
if (!user) return Response.json({ error: "用户不存在" }, { status: 404 });
return Response.json(user);
}
// 获取用户列表,支持 ?name= 查询参数
if (method === "GET") {
const name = url.searchParams.get("name");
const result = name ? users.filter((u) => u.name.includes(name)) : users;
return Response.json(result);
}
// 新增用户
if (method === "POST") {
const body = await request.json();
const user = { id: users.length + 1, name: body.name };
users.push(user);
return Response.json(user, { status: 201 });
}
// 修改用户
if (method === "PUT" && id) {
const body = await request.json();
const user = users.find((u) => u.id === id);
if (!user) return Response.json({ error: "用户不存在" }, { status: 404 });
user.name = body.name;
return Response.json(user);
}
// 删除用户
if (method === "DELETE" && id) {
const index = users.findIndex((u) => u.id === id);
if (index === -1) return Response.json({ error: "用户不存在" }, { status: 404 });
users.splice(index, 1);
return Response.json({ ok: true });
}
return Response.json({ error: "请求方式不支持" }, { status: 405 });
},
};
用 curl 逐个验证
# 获取列表
curl http://localhost:8787/api/users
# 获取单个
curl http://localhost:8787/api/users/1
# 新增
curl -X POST http://localhost:8787/api/users -H "Content-Type: application/json" -d '{"name":"Cathy"}'
# 修改
curl -X PUT http://localhost:8787/api/users/1 -H "Content-Type: application/json" -d '{"name":"Alice2"}'
# 删除
curl -X DELETE http://localhost:8787/api/users/1
要特别说明的是,内存数组的数据在本地运行时有效,部署到线上后每个请求可能落在不同节点,数据无法共享,重启后也会丢失。真正的持久化方案用 Cloudflare Workers KV 存储 或 Cloudflare Workers D1 数据库,逻辑上把这里的数组换成对应 API 即可。
CORS 跨域处理
如果前端页面和后端接口不在同一个域名,浏览器会拦截跨域请求。解决方式是在响应中加上 CORS 响应头
const corsHeaders = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type",
};
export default {
async fetch(request, env, ctx) {
// 预检请求直接返回成功
if (request.method === "OPTIONS") {
return new Response(null, { headers: corsHeaders });
}
const url = new URL(request.url);
const parts = url.pathname.split("/").filter(Boolean);
if (parts[0] === "api" && parts[1] === "users") {
// 业务逻辑同上一节,最后响应时带上 corsHeaders
return Response.json({ hello: "users" }, { headers: corsHeaders });
}
return new Response("页面不存在", { status: 404, headers: corsHeaders });
},
};
要点有两个。浏览器先发 OPTIONS 预检请求,要直接返回成功。所有实际响应的 headers 都要带上 CORS 头,缺一个浏览器都会拦截。Access-Control-Allow-Origin 写 * 表示允许任何域名,生产环境建议写你的具体域名。
路由代码优化
上面的手写路由在接口变多后会越来越长,两个优化方向
| 方式 | 说明 |
|---|---|
| URLPattern | 平台内置的路径匹配 API,支持通配符和参数提取 |
| Hono | 社区最流行的 Workers 框架,路由、中间件一应俱全 |
对于接口数量多、结构复杂的项目,建议直接使用 Hono 这类框架。新手阶段手写路由能帮你理解原理,跑通上面这个例子就达到了本篇目标。
上一篇 Cloudflare Workers 请求与响应
下一篇 Cloudflare Workers 环境变量与密钥管理