简介
支持表单需要一个 API,用于区分有效请求、错误的 JSON、缺失的路由和不可用的工单服务。你将使用 JavaScript 构建这个 HTTP 边界,在本地测试,然后将它与一个临时上游 Worker 一起部署。
使用你自己的 Cloudflare 学习账号,以及在连接实验中学到的设备授权知识。本实验从一个全新的 VM 开始,其中已在 /home/labex/project/support-api 安装 Node.js 22.22.0 和项目本地的 Wrangler 4.131.1。前置要求是了解基本的 JavaScript 函数、对象和模块;本实验会讲解 HTTP 行为和异步请求。两个公开 Worker 仅使用合成数据。提供的上游服务会确认请求,但不会存储任何内容,因此它不是持久化工单系统。完成这个小练习只需要 Workers Free 和 workers.dev 子域名,不需要数据库、购买的域名或付费升级。请求会计入你账号的 Workers 使用量。
结束实验前,你将删除两个 Worker 并退出登录。保持使用同一个终端,以保留用于资源名称和 URL 的 Shell 变量。
按路径和方法路由请求
在本步骤中,你将为每个受支持的 URL 指定明确的方法和响应。路径标识要执行的操作,方法描述执行的动作。GET /health 用于检查可用性,POST /requests 将接收支持请求。
进入准备好的项目并确认工具版本:
cd /home/labex/project/support-api
node --version
npx wrangler --version
预期 Node 输出 v22.22.0,Wrangler 输出 4.131.1。工具已经安装完成;如果在自己的计算机上操作,请安装 Node,并在项目中运行 npm install --save-dev wrangler@4.131.1。使用项目锁定文件复现项目时,请运行 npm ci。
生成一个唯一的临时名称。openssl rand -hex 6 会生成 12 个随机十六进制字符;$(...) 会插入该输出,Shell 赋值则会保存这个名称,供后续命令使用。
WORKER_NAME="labex-support-$(openssl rand -hex 6)"
写入标准的 Wrangler 配置。cat > file <<MARKER 会写入后续各行,直到遇到结束标记;未加引号的标记允许 Shell 替换 $WORKER_NAME。
cat > wrangler.jsonc <<CONFIG
{
"name": "$WORKER_NAME",
"main": "src/index.js",
"compatibility_date": "2026-09-14",
"workers_dev": true,
"preview_urls": false,
"vars": {"UPSTREAM_URL": "http://127.0.0.1:8081"}
}
CONFIG
main 指定处理程序,compatibility_date 选择运行时行为,vars 则通过 env 提供非机密的上游地址。目前该地址指向稍后启动的本地固定服务。这里禁用公开预览 URL,以简化资源清单。
写入处理程序。带引号的 JS 标记会按原样保留 JavaScript。new URL(...).pathname 提取路由。三元表达式选择允许的方法;HTTP 405 响应还会通过 Allow 标头公布该方法。Response.json 会将对象序列化为 JSON,并设置内容类型。async 处理程序可以在后续步骤中等待异步操作。
cat > src/index.js <<'JS'
export default {
async fetch(request, env) {
const path = new URL(request.url).pathname;
if (path !== '/health' && path !== '/requests') {
return Response.json({error: 'not_found'}, {status: 404});
}
const allowed = path === '/health' ? 'GET' : 'POST';
if (request.method !== allowed) {
return Response.json({error: 'method_not_allowed'}, {
status: 405, headers: {Allow: allowed}
});
}
if (path === '/health') return Response.json({status: 'ok'});
return Response.json({error: 'not_implemented'}, {status: 501});
}
};
JS
在后台启动本地 Wrangler:> 会重定向输出,2>&1 会把错误也包含进来,& 会在服务器运行时立即返回终端提示符。
npx wrangler dev --port 8080 > api.log 2>&1 &
cat api.log
等待日志报告 8080 端口已准备就绪。如果服务器仍在启动,请重新运行 cat api.log。curl -i 会同时显示 HTTP 状态和标头:
curl -i http://127.0.0.1:8080/health
curl -i http://127.0.0.1:8080/missing
curl -i http://127.0.0.1:8080/requests
预期结果依次为:状态码 200 和 {"status":"ok"};状态码 404 和 {"error":"not_found"};状态码 405、{"error":"method_not_allowed"} 以及 Allow: POST。这些错误响应是有意设计的。服务器仍在运行时,点击验证按钮。
解析并验证 JSON 输入
在本步骤中,你将先拒绝格式错误的输入,再调用任何上游服务。HTTP 415 表示不支持该媒体类型,400 表示无法解析 JSON,422 表示解析后的数据不符合约定。subject 必须是字符串,去除空白后长度为 1–80 个字符。
使用下面的完整版本替换处理程序。headers.get 读取声明的媒体类型;在 ; 处分割可以允许字符集参数。await request.json() 会等待解析,并且只读取一次请求正文。try/catch 会将解析异常转换为可预测的响应。JSON 还可以表示 null、数组或数字,因此验证会先检查数据形状,再使用字符串方法。trim() 会规范化通过验证的 subject。
cat > src/index.js <<'JS'
export default {
async fetch(request, env) {
const path = new URL(request.url).pathname;
if (path !== '/health' && path !== '/requests') {
return Response.json({error: 'not_found'}, {status: 404});
}
const allowed = path === '/health' ? 'GET' : 'POST';
if (request.method !== allowed) {
return Response.json({error: 'method_not_allowed'}, {
status: 405, headers: {Allow: allowed}
});
}
if (path === '/health') return Response.json({status: 'ok'});
const mediaType = (request.headers.get('content-type') || '').split(';')[0].trim().toLowerCase();
if (mediaType !== 'application/json') {
return Response.json({error: 'unsupported_media_type'}, {status: 415});
}
let body;
try {
body = await request.json();
} catch {
return Response.json({error: 'invalid_json'}, {status: 400});
}
if (!body || Array.isArray(body) || typeof body.subject !== 'string' ||
body.subject.trim().length < 1 || body.subject.trim().length > 80) {
return Response.json({error: 'invalid_subject'}, {status: 422});
}
const subject = body.subject.trim();
return Response.json({subject}, {status: 201});
}
};
JS
源代码发生变化后,Wrangler 会重新加载。运行 cat api.log 检查是否存在编译错误。发送有效请求:-H 提供标头,--data 提供请求正文并选择 POST 方法。单引号可以让 Shell 保留 JSON 中的双引号。
curl -i http://127.0.0.1:8080/requests -H "Content-Type: application/json" --data '{"subject":" Printer offline "}'
预期状态码为 201,响应为 {"subject":"Printer offline"}。这是内存中的确认,不是已保存的工单。测试三种不同的拒绝路径:
curl -i http://127.0.0.1:8080/requests -H "Content-Type: application/json" --data '{'
curl -i http://127.0.0.1:8080/requests -H "Content-Type: application/json" --data '{"subject":" "}'
curl -i http://127.0.0.1:8080/requests -H "Content-Type: text/plain" --data 'hello'
预期结果分别为 400 invalid_json、422 invalid_subject 和 415 unsupported_media_type。另外尝试 JSON null、[] 和 {"subject":5};每种情况都必须返回 422,而不能抛出异常。点击验证按钮;验证器会检查这些边界条件,同时保留健康检查和路由行为。
调用上游并隔离其错误
在本步骤中,你将把 API 连接到提供的工单服务模拟器。上游服务是你的服务所调用的依赖项。对于普通主题,模拟器会返回一个合成工单;对于特殊主题 simulate-outage,会返回 HTTP 503;它从不存储请求。
查看提供的源代码以了解固定服务,然后为它配置一个唯一的 Worker 名称:
cat upstream/index.js
cat > upstream/wrangler.jsonc <<CONFIG
{
"name": "${WORKER_NAME}-upstream",
"main": "index.js",
"compatibility_date": "2026-09-14",
"workers_dev": true,
"preview_urls": false
}
CONFIG
--config 用于选择第二个配置文件。使用 8081 端口和单独的调试器端口,这样两个本地 Worker 就可以同时运行:
npx wrangler dev --config upstream/wrangler.jsonc --port 8081 --inspector-port 9230 > upstream.log 2>&1 &
cat upstream.log
curl -i http://127.0.0.1:8081/health
等待服务准备就绪,预期状态码为 200,响应为 {"service":"support-upstream","status":"ok"}。现在使用下面的完整集成版本替换主处理程序。全局 fetch 函数用于发出出站请求;JSON.stringify 会对经过验证的主题进行编码。await 会等待响应。HTTP 错误不会抛出异常,因此需要通过 upstream.ok 显式检查状态;catch 则单独处理连接失败或无法读取 JSON 响应的情况。HTTP 502 会告知客户端依赖项失败,同时不会暴露上游响应正文。
cat > src/index.js <<'JS'
export default {
async fetch(request, env) {
const path = new URL(request.url).pathname;
if (path !== '/health' && path !== '/requests') {
return Response.json({error: 'not_found'}, {status: 404});
}
const allowed = path === '/health' ? 'GET' : 'POST';
if (request.method !== allowed) {
return Response.json({error: 'method_not_allowed'}, {
status: 405, headers: {Allow: allowed}
});
}
if (path === '/health') return Response.json({status: 'ok'});
const mediaType = (request.headers.get('content-type') || '').split(';')[0].trim().toLowerCase();
if (mediaType !== 'application/json') {
return Response.json({error: 'unsupported_media_type'}, {status: 415});
}
let body;
try {
body = await request.json();
} catch {
return Response.json({error: 'invalid_json'}, {status: 400});
}
if (!body || Array.isArray(body) || typeof body.subject !== 'string' ||
body.subject.trim().length < 1 || body.subject.trim().length > 80) {
return Response.json({error: 'invalid_subject'}, {status: 422});
}
const subject = body.subject.trim();
try {
const upstream = await fetch(`${env.UPSTREAM_URL}/tickets`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({subject})
});
if (!upstream.ok) {
return Response.json({error: 'upstream_unavailable'}, {status: 502});
}
const ticket = await upstream.json();
return Response.json({ticket: ticket.ticket, subject}, {status: 201});
} catch {
return Response.json({error: 'upstream_unavailable'}, {status: 502});
}
}
};
JS
curl -i http://127.0.0.1:8080/requests -H "Content-Type: application/json" --data '{"subject":"Printer offline"}'
curl -i http://127.0.0.1:8080/requests -H "Content-Type: application/json" --data '{"subject":"simulate-outage"}'
预期第一个请求返回 201 和 {"ticket":"demo-1001","subject":"Printer offline"},第二个请求返回 502 和 {"error":"upstream_unavailable"}。模拟器的内部诊断信息不得出现在响应中。主题是合成数据,此端点不会产生持久化副作用。两个本地服务器都运行时,点击验证按钮。
本实验使用普通 HTTP 来练习外部服务边界。后续实验会讲解用于 Worker 之间内部调用的服务绑定。Diagnose Worker Failures 实验会讲解有限超时和更丰富的诊断信息。这里的固定服务只返回大小受限的小型响应;生产 API 还必须限制不可信请求和响应的大小。
部署并测试公开 API
在本步骤中,你将把两个 Worker 部署到同一个学习账号,并将本地上游地址替换为其公开 URL。先停止两个本地任务。查看 jobs 的结果,并使用实际的任务编号;以下示例假设 API 是 1,上游是 2。
jobs
kill %1 %2
为这个全新的 VM 授权。该授权会识别你的账号,并允许部署和删除 Worker。末尾的权限与部署课程中的授权范围一致,但本实验不需要日志流。
npx wrangler login --device --browser=false --scopes account:read user:read workers_scripts:write workers_tail:read
打开显示的浏览器链接,输入当前设备代码,查看 Wrangler 请求的权限(包括所需的 Background Access),仅选择你的学习账号,然后完成授权。返回终端并等待授权完成。
npx wrangler whoami --json
确认输出中的 loggedIn: true、账号名称,以及 accounts 中显示的实际账号 ID。将下面的 YOUR_ACCOUNT_ID 替换为该 ID。保留步骤 1 中生成的名称;如果变量丢失,请读取已保存的配置并恢复它,不要重新生成其他资源名称。
cat > upstream/wrangler.jsonc <<CONFIG
{
"name": "${WORKER_NAME}-upstream",
"main": "index.js",
"compatibility_date": "2026-09-14",
"workers_dev": true,
"preview_urls": false,
"account_id": "YOUR_ACCOUNT_ID"
}
CONFIG
npx wrangler deploy --config upstream/wrangler.jsonc
从部署输出中复制完整、准确的 workers.dev URL。复用账号已有的子域名。如果 Wrangler 提示首次注册子域名,请选择一个可用名称并按照确认流程操作;不要更改账号已有的子域名。
现在重写主配置,将两个占位符替换为你的账号 ID 和上游 URL(末尾不要带斜杠)。global_fetch_strictly_public 会让出站 fetch() 使用公共互联网路由,包括访问该账号 workers.dev 子域名上的另一个 Worker。如果没有此配置,即使两个 Worker 可以分别正常运行,这个同区域 HTTP 调用也可能失败。该标志应放在已部署的 API 配置中;之前的本地回环固定服务不需要它。请参阅 Fetch API 指南。
cat > wrangler.jsonc <<CONFIG
{
"name": "$WORKER_NAME",
"main": "src/index.js",
"compatibility_date": "2026-09-14",
"compatibility_flags": ["global_fetch_strictly_public"],
"workers_dev": true,
"preview_urls": false,
"account_id": "YOUR_ACCOUNT_ID",
"vars": {"UPSTREAM_URL": "YOUR_UPSTREAM_URL"}
}
CONFIG
cat wrangler.jsonc
npx wrangler deploy
将主 API 的部署输出中的 URL 复制到下面的变量中:
API_URL="https://YOUR_API.YOUR_SUBDOMAIN.workers.dev"
curl -i "$API_URL/health"
curl -i "$API_URL/requests" -H "Content-Type: application/json" --data '{"subject":"Printer offline"}'
curl -i "$API_URL/requests" -H "Content-Type: application/json" --data '{"subject":"simulate-outage"}'
curl -i "$API_URL/requests" -H "Content-Type: application/json" --data '{'
预期结果与本地测试相同:健康检查返回 200,合成工单返回 201,上游错误返回 502,格式错误的 JSON 返回 400。遇到连接错误时,请等待主机名传播后再重试。在 Dashboard 中选择同一个学习账号,然后打开 Compute → Workers & Pages。找到两个准确的名称,并将其地址与部署输出进行比较。这是一个只读检查点;不要在其中创建重复应用。
下面的示例展示了主 API 及其对应的 -upstream 服务。在左侧边栏展开 Compute,选择 Workers & Pages。如果账号中包含其他项目,请使用 Search applications。将完整生成的名称及其下方的地址与两次部署输出进行比较;你的随机后缀和账号子域名会与此示例不同。

两个资源都应出现在当前选中的账号中。它们的存在可以确认部署位置;上面的 HTTP 响应则用于确认 API 是否正常工作。如果缺少任意一个名称,请检查账号选择器和部署输出,然后再重试。不要使用 Create application 来复制 CLI 部署的应用。
点击验证按钮。验证器会独立检查两个 Worker 的归属、已部署的上游绑定,以及公开 API 的正向和负向响应。它只会向本实验的模拟器发送合成的无状态请求。
删除两个临时 Worker
在本步骤中,你将在授权仍然有效时删除 API 及其上游,以便验证删除结果。这些是本实验创建的唯一云资源。删除前先检查两个配置:
cat wrangler.jsonc
cat upstream/wrangler.jsonc
确认主 Worker 使用 labex-support-... 名称,并且对应的上游名称带有 -upstream 后缀,同时使用同一个学习账号 ID。先删除主 API,再删除上游。在每个确认提示处检查准确的名称,然后按一次 y 键。
npx wrangler delete
npx wrangler delete --config upstream/wrangler.jsonc
Wrangler 4.131.1 可能会删除 Worker,然后在检查旧版 Workers Sites KV 数据时打印身份验证错误,因为当前授权没有 KV 访问权限。这个特定诊断信息不能证明删除成功或失败。不要仅为了消除该信息而授予更多权限。刷新 Workers & Pages 并点击验证按钮:成功的授权资源清单必须确认两个名称都不存在。网络错误或授权错误无法得出结论;请先解决这些问题再继续。保留其他应用、学习账号及其子域名。
断开 VM 授权
在本步骤中,确认两个资源都已清理后,你将移除这个 VM 的 Wrangler 授权。退出登录不会删除 Worker,因此必须先完成清理。
npx wrangler logout
npx wrangler whoami --json
预期结果明确显示 "loggedIn": false。未认证的状态命令可能以非零状态退出;只要其结构化结果清楚报告已退出登录,这就是预期行为。网络错误不等同于退出登录。点击验证按钮,然后结束 LabEx 环境。你的浏览器登录状态和学习账号仍可用于后续实验;每个新 VM 都需要单独授权。
总结
你构建了一个按 HTTP 方法处理请求的 API,解析并验证了 JSON,规范化了通过验证的数据,并将上游故障转换为可预测的公开错误。你在本地和 Cloudflare 上测试了正常请求及被拒绝的请求,检查了两个部署的归属,删除了临时资源,并断开了 VM 的授权。
如需参考,请查看官方的 Request API、Response API 和 Fetch API。

