六维教程

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 环境变量与密钥管理

上一篇
Cloudflare Workers 请求与响应
下一篇
Cloudflare Workers 环境变量与密钥管理