Cloudflare Worker 集成与使用指南(Silent 路由)
参考官方入门指南:https://developers.cloudflare.com/workers/get-started/guide/
环境准备
- Rust + Wasm 目标:
rustup target add wasm32-unknown-unknown - Wrangler(推荐全局安装):
- npm:
npm i -g wrangler - 或 Homebrew:
brew install cloudflare/wrangler/wrangler
- npm:
- 可选:
worker-build(wrangler 构建时会调用):cargo install -q worker-build
示例位置
- 目录:
examples/cloudflare-worker - 入口:
examples/cloudflare-worker/src/lib.rs - 路由:
examples/cloudflare-worker/src/route.rs - 配置:
examples/cloudflare-worker/wrangler.toml
示例路由一览
| 路径 | 方法 | 功能 | 使用的绑定 |
|---|---|---|---|
/hello |
GET | 基础文本响应 | 无 |
/kv/<key> |
GET | 读取 KV 值 | KV (MY_KV) |
/kv/<key> |
PUT | 写入 KV 值(请求体为值) | KV (MY_KV) |
/kv/<key> |
DELETE | 删除 KV 键 | KV (MY_KV) |
/d1/users |
GET | 查询用户列表(最多100条) | D1 (MY_DB) |
/d1/users |
POST | 创建用户(JSON: {"name":"..","email":".."}) |
D1 (MY_DB) |
/r2/<key> |
GET | 读取 R2 对象 | R2 (MY_BUCKET) |
/r2/<key> |
PUT | 上传对象到 R2(请求体为文件内容) | R2 (MY_BUCKET) |
/r2/<key> |
DELETE | 删除 R2 对象 | R2 (MY_BUCKET) |
路由与适配
- 使用
WorkRoute适配 Cloudflare Worker 的Request/Response与 Silent 的Request/Response。 - 适配中使用
Response::status()与Response::take_body()完成状态与字节聚合。 - 响应体通过
http-body-util::BodyExt::collect聚合,兼容 Once/Chunks/Stream/Incoming/Boxed。 - 错误响应保留原始状态码(如 404、400),不再统一返回 500。
WorkRoute 增强功能
with_configs() 方法
- 用于将 Cloudflare Worker 的绑定(Env 或其子资源)注入到路由中
- 处理器通过
req.get_config::<T>()获取注入的配置 - 推荐直接注入
Env,在处理器中按需获取 KV/D1/R2 等绑定
use silent::prelude::*;
use worker::Env;
let mut cfg = Configs::default();
cfg.insert(env); // 注入整个 Env
let wr = WorkRoute::new(route).with_configs(cfg);处理器中获取绑定:
async fn my_handler(req: Request) -> silent::Result<Response> {
let env = req.get_config::<Env>()?;
let kv = env.kv(“MY_KV”).map_err(worker_err)?;
let d1 = env.d1(“MY_DB”).map_err(worker_err)?;
let bucket = env.bucket(“MY_BUCKET”).map_err(worker_err)?;
// ...
}Context 注入
- 将
worker::Context与Env一样注入到Configs中 - 处理器通过
req.get_config::<worker::Context>()获取 - 适用于需要调度后台任务(
ctx.wait_until(fut))的场景
let mut cfg = Configs::default();
cfg.insert(env);
cfg.insert(ctx); // Context 也注入 Configs
let wr = WorkRoute::new(route).with_configs(cfg);
// 处理器中使用 Context
async fn my_handler(req: Request) -> silent::Result<Response> {
let ctx = req.get_config::<worker::Context>()?;
ctx.wait_until(async { /* 后台任务 */ });
Ok(Response::empty())
}只读 Configs(重要)
- 由于 Wasm/Workers 的执行模型,实例的跨请求复用不可保证,且可能冷启动。处理器内对
Configs的修改不会在后续请求中保持。 - 将
Configs视为只读配置的载体,仅在初始化阶段注入不可变参数(常量、开关、外部服务句柄等)。 - 如需跨请求可变状态,请使用 Cloudflare 的持久化能力:KV、Durable Objects、D1、R2、Queues 等。
Env 与 Context 的使用
Env:用于获取绑定(KV/DO/D1/R2/Queues 等)。建议将只读句柄注入到Configs,供路由/处理器读取。Context:与Env一样通过Configs注入,处理器通过req.get_config::<Context>()获取。用于调度后台任务(ctx.wait_until(fut)),任务可在响应返回后继续执行。
示例:注入 Env + Context 并访问 KV 绑定
use worker::{Context, Env, Request, Response, Result};
use silent::prelude::*;
#[worker::event(fetch)]
pub async fn main(req: Request, env: Env, ctx: Context) -> Result<Response> {
console_error_panic_hook::set_once();
// 将 Env 和 Context 注入到 Configs
let mut cfg = Configs::default();
cfg.insert(env);
cfg.insert(ctx);
let wr = WorkRoute::new(get_route()).with_configs(cfg);
Ok(wr.call(req).await)
}
fn get_route() -> Route {
Route::new_root()
.append(Route::new("hello").get(hello))
.append(Route::new("kv").append(
Route::new("<key>").get(kv_get).put(kv_put),
))
}
async fn hello(_req: silent::Request) -> silent::Result<&'static str> {
Ok("hello from Worker")
}
/// KV 读取示例
async fn kv_get(req: silent::Request) -> silent::Result<silent::Response> {
let env = req.get_config::<Env>()?;
let key: String = req.get_path_params("key")?;
let kv = env.kv("MY_KV").map_err(|e| {
silent::SilentError::business_error(
silent::StatusCode::INTERNAL_SERVER_ERROR, e.to_string(),
)
})?;
match kv.get(&key).text().await {
Ok(Some(v)) => Ok(silent::Response::json(&serde_json::json!({"key": key, "value": v}))),
Ok(None) => Err(silent::SilentError::business_error(
silent::StatusCode::NOT_FOUND, format!("key '{key}' not found"),
)),
Err(e) => Err(silent::SilentError::business_error(
silent::StatusCode::INTERNAL_SERVER_ERROR, e.to_string(),
)),
}
}
/// KV 写入示例
async fn kv_put(mut req: silent::Request) -> silent::Result<silent::Response> {
let env = req.get_config::<Env>()?.clone();
let key: String = req.get_path_params("key")?;
let value = read_body_text(&mut req).await?;
let kv = env.kv("MY_KV").map_err(|e| {
silent::SilentError::business_error(
silent::StatusCode::INTERNAL_SERVER_ERROR, e.to_string(),
)
})?;
kv.put(&key, &value).map_err(|e| {
silent::SilentError::business_error(
silent::StatusCode::INTERNAL_SERVER_ERROR, e.to_string(),
)
})?.execute().await.map_err(|e| {
silent::SilentError::business_error(
silent::StatusCode::INTERNAL_SERVER_ERROR, e.to_string(),
)
})?;
Ok(silent::Response::json(&serde_json::json!({"status": "ok", "key": key})))
}本地预览(wrangler dev)
cd examples/cloudflare-worker
wrangler dev默认监听 http://127.0.0.1:8787,测试示例:
# 基础路由
curl http://127.0.0.1:8787/hello
# KV 操作
curl -X PUT http://127.0.0.1:8787/kv/mykey -d "hello world"
curl http://127.0.0.1:8787/kv/mykey
curl -X DELETE http://127.0.0.1:8787/kv/mykey
# D1 操作(需先建表,见 wrangler.toml 注释)
curl http://127.0.0.1:8787/d1/users
curl -X POST http://127.0.0.1:8787/d1/users \
-H "Content-Type: application/json" \
-d '{"name":"Alice","email":"alice@example.com"}'
# R2 操作
curl -X PUT http://127.0.0.1:8787/r2/test.txt -d "file content"
curl http://127.0.0.1:8787/r2/test.txt
curl -X DELETE http://127.0.0.1:8787/r2/test.txt构建与部署
- 登录账号:
wrangler login - 构建 + 部署:
cd examples/cloudflare-worker
wrangler deploy部署完成后,wrangler 会输出可访问的生产地址。
wrangler.toml 配置
最小配置:
name = "example-cloudflare-worker"
main = "build/index.js"
compatibility_date = "2025-09-24"
[build]
command = "cargo install -q worker-build && worker-build --release"绑定配置(KV/D1/R2):
# KV 命名空间绑定
# 创建命令: wrangler kv namespace create MY_KV
[[kv_namespaces]]
binding = "MY_KV"
id = "your-kv-namespace-id"
preview_id = "your-kv-preview-id"
# D1 数据库绑定
# 创建命令: wrangler d1 create my-database
# 建表: wrangler d1 execute MY_DB --local --command "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT NOT NULL)"
[[d1_databases]]
binding = "MY_DB"
database_name = "my-database"
database_id = "your-d1-database-id"
# R2 存储桶绑定
# 创建命令: wrangler r2 bucket create my-bucket
[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "my-bucket"wrangler 将使用 worker-build 将 Rust 工程编译为 Wasm,并生成可运行的 Worker 入口。
环境变量与机密
- 普通变量:在
wrangler.toml的[vars]中定义 - 机密:
wrangler secret put MY_SECRET - 处理器中可通过
Env获取,或在with_configs时注入只读配置(推荐仅注入只读句柄)。
常见问题
- Wasm/Workers 下请求生命周期短且实例不可预测:不要依赖进程内“全局可变状态”。
- 需要持久化:使用 Cloudflare KV / Durable Objects / D1 / R2 等。
- 构建失败缺少目标:确保
wasm32-unknown-unknown已安装。 - 首次构建较慢:wrangler 会拉取依赖并安装工具(如 wasm-bindgen)。
请求体读取
Worker 环境下读取请求体需使用 req.take_body() + BodyExt::collect():
use http_body_util::BodyExt;
async fn read_body_bytes(req: &mut silent::Request) -> silent::Result<Vec<u8>> {
let body = req.take_body();
let collected = body.collect().await.map_err(|e| {
silent::SilentError::business_error(
silent::StatusCode::BAD_REQUEST,
format!("read body error: {e}"),
)
})?;
Ok(collected.to_bytes().to_vec())
}JSON 请求体可直接使用 req.json_parse::<T>().await。
范围与验收(本仓示例)
examples/cloudflare-worker,产物cdylib,目标wasm32-unknown-unknown。- 能执行
cargo build -p example-cloudflare-worker --target wasm32-unknown-unknown成功。 - 在
wrangler dev下本地预览,所有路由正常工作:/hello返回文本/kv/<key>支持 GET/PUT/DELETE/d1/users支持 GET/POST/r2/<key>支持 GET/PUT/DELETE