cloudflare

用 Rust 写 Cloudflare Workers:workers-rs + KV 实战指南

用一个「国家到城市」KV API 走通 workers-rs 实战流程:从 Rust/Wasm 环境、官方模板、Wrangler 本地调试,到 KV 绑定、部署和生产注意事项。

约二千一百字·读约六分钟 · English

用 Rust 写 Cloudflare Workers:workers-rs + KV 实战指南

为什么会想用 Rust 写 Worker

Cloudflare Workers 最常见的写法当然是 JavaScript 或 TypeScript:创建快、部署快,和 Web 平台也天然贴合。但如果你的项目里已经有 Rust 代码,或者你想把一些更偏系统层的逻辑放到边缘侧,workers-rs 就很值得看一眼。

它做的事情很直接:让你用 Rust 写 Worker,再编译成 WebAssembly,最后交给 Cloudflare Workers 运行时部署到全球边缘网络。换句话说,你依然在用 Workers 这套平台能力,只是把业务代码换成了 Rust。

我会在这些场景优先考虑它:

  • 请求签名、协议解析、数据转换这类逻辑需要更强的类型约束。
  • 团队已经熟悉 Rust,不想在边缘层再维护一套 JS 版本。
  • 希望直接使用 Workers KV、R2、D1、Queues、Durable Objects、Workers AI 等绑定。
  • 可以接受 Wasm 构建链路略复杂一点,换来更好的编译期检查。

这篇不追求把 workers-rs 所有能力讲完,而是先做一个能跑、能测、能部署的小项目:一个「国家 → 城市」存储 API,用 Workers KV 负责读写。

文中的命令和关键 API 已在 2026-07-30 按 Cloudflare 官方文档与 workers-rs 仓库核对。Wrangler、workers-rs 和 Workers 运行时都在持续更新,真正开新项目时,建议再对照一次官方当前文档。

先把环境准备好

workers-rs 的本质是 Rust + WebAssembly + Wrangler。所以开始前,你需要把三件事准备好:Rust 工具链、Wasm 编译目标,以及用于生成模板的 cargo-generate

# 安装或更新 Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# 添加 Wasm 编译目标
rustup target add wasm32-unknown-unknown

# 安装 cargo-generate
cargo install cargo-generate

Wrangler 是 Cloudflare 的开发和部署工具。这里直接用 npx 调用即可,不强制全局安装:

npx wrangler --version

第一次执行 wrangler devwrangler deploy 时,Wrangler 会引导你登录 Cloudflare 账号。如果你是在没有浏览器 UI 的环境里操作,可以参考 Wrangler 登录文档走替代流程。

用官方模板创建项目

新项目直接从 workers-rs 官方模板开始:

cargo generate cloudflare/workers-rs

按提示选择模板。第一次练手推荐选 template/hello-world-http,项目名可以叫:

rust-kv-demo

生成后先看三个文件:

文件你需要关心什么
Cargo.tomlRust 依赖、crate 信息、Wasm release 优化配置
wrangler.tomlwrangler.jsoncWorker 名称、构建命令、绑定和部署配置
src/lib.rsWorker 的 Rust 入口

进入项目:

cd rust-kv-demo

模板会通过 worker-build 处理 Rust/Wasm 的打包流程。正常情况下,你不用自己补一层 JavaScript glue code,这也是官方模板省心的地方。

先确认 Hello World 能跑

模板里的 src/lib.rs 大概会长这样:

use worker::*;

#[event(fetch)]
async fn main(_req: Request, _env: Env, _ctx: Context) -> Result<Response> {
    Response::ok("Hello, World!")
}

本地启动 Worker:

npx wrangler dev

打开 http://localhost:8787,能看到 Hello, World! 就说明这条链路已经打通了:

Rust 代码 → Wasm 构建 → Wrangler 本地运行 → Workers 请求处理

后面修改 src/lib.rs 时,Wrangler 会重新构建本地 Worker。第一次编译可能慢一点,后续体验会顺很多。

我们要做什么:一个 KV 小 API

为了让例子足够完整,但又不被业务细节淹没,这里做两个接口:

方法路径行为
POST/:country请求体为 {"city": "Paris"},把国家和城市写入 KV
GET/:country从 KV 读取国家对应的城市

这个例子会顺手覆盖 workers-rs 的几个常用点:RequestResponseEnvRouter、路径参数、JSON 解析,以及 Workers KV 绑定。

创建 KV Namespace

先创建一个名为 cities 的 KV namespace:

npx wrangler kv namespace create cities

命令执行后,Wrangler 会返回一段配置。把里面的 id 写进你的 Worker 配置文件。

如果项目使用 wrangler.toml

[[kv_namespaces]]
binding = "cities"
id = "你的-namespace-id"

如果项目使用 wrangler.jsonc

{
  "kv_namespaces": [
    {
      "binding": "cities",
      "id": "你的-namespace-id"
    }
  ]
}

这里最容易踩坑的是 binding 名称。后面的 Rust 代码会用这个名字拿 KV:

ctx.kv("cities")?

所以配置里叫 cities,代码里也必须叫 cities。大小写、拼写都要一致。

添加 JSON 解析依赖

接口的 POST body 是 JSON,所以加上 serde

cargo add serde --features derive

模板通常已经带了 worker 依赖。Cargo.toml 中大概会有类似内容:

[dependencies]
worker = "0.6"
serde = { version = "1", features = ["derive"] }

这里的 worker = "0.6" 只是示例。实际项目建议保留模板生成出来的版本,或者按 crates.io 当前稳定版本调整,不要把文章里的版本号当成必须照抄的固定值。

写完整的 src/lib.rs

src/lib.rs 改成下面这样:

use serde::{Deserialize, Serialize};
use worker::*;

#[event(fetch)]
async fn fetch(req: Request, env: Env, _ctx: Context) -> Result<Response> {
    let router = Router::new();

    #[derive(Serialize, Deserialize, Debug)]
    struct Country {
        city: String,
    }

    router
        .post_async("/:country", |mut req, ctx| async move {
            let country = ctx.param("country").unwrap();
            let city = match req.json::<Country>().await {
                Ok(c) => c.city,
                Err(_) => String::from(""),
            };

            if city.is_empty() {
                return Response::error("Bad Request", 400);
            }

            match ctx.kv("cities")?.put(country, &city)?.execute().await {
                Ok(_) => Response::ok(city),
                Err(_) => Response::error("Bad Request", 400),
            }
        })
        .get_async("/:country", |_req, ctx| async move {
            if let Some(country) = ctx.param("country") {
                match ctx.kv("cities")?.get(country).text().await? {
                    Some(city) => Response::ok(city),
                    None => Response::error("Country not found", 404),
                }
            } else {
                Response::error("Bad Request", 400)
            }
        })
        .run(req, env)
        .await
}

拆开看其实不复杂:

  • #[event(fetch)] 声明这是 HTTP 请求入口。
  • Router::new() 用来组织不同路径和方法。
  • ctx.param("country") 读取 /:country 里的路径参数。
  • ctx.kv("cities") 通过绑定名获取 KV namespace。
  • put(...).execute().await 写入,get(...).text().await 读取。

这段 demo 的错误处理刻意写得直白:JSON 解析失败或 city 为空就返回 400,找不到国家就返回 404。生产项目可以在这个基础上继续封装统一的 JSON 响应。

本地测试一下

启动开发服务器:

npx wrangler dev

写入一条数据:

curl --json '{"city": "Paris"}' http://localhost:8787/France

再读回来:

curl http://localhost:8787/France

如果返回:

Paris

说明路由、JSON 解析和 KV 绑定都已经连起来了。

如果这里报 KV 绑定错误,先别急着怀疑 Rust 代码,优先检查这三处:

  • wrangler.tomlwrangler.jsonc 里是否真的写了 kv_namespaces
  • binding 是否正好叫 cities
  • 当前 wrangler dev 读取的是否就是你刚刚编辑的配置文件。

部署到 Cloudflare

本地确认没问题后,直接部署:

npx wrangler deploy

部署成功后,可以访问你的 *.workers.dev 地址,也可以接入自定义域名。第一次启用 workers.dev 子域名时,DNS 生效可能会有一点延迟,等一会儿再测就好。

生产环境别忽略这些细节

控制 Wasm 体积

workers-rs 模板通常会在 release profile 里配置一些体积优化,比如 ltostripcodegen-units。Cloudflare 的 Rust 文档也说明,worker-build 会参与 Wasm 打包和优化流程。

但这不代表可以随便加依赖。能不能编译到 wasm32-unknown-unknown、会不会带进一堆不必要的功能、最终包体是否可控,都是边缘应用需要提前考虑的问题。

crate 兼容性要提前确认

很多 Rust crate 可以在 Workers 上使用,但并不是所有 crate 都天然适配 Wasm。凡是依赖系统线程、本地文件系统、原生 TLS、阻塞网络调用的库,都要多看一眼。

Cloudflare 有 Supported crates 文档。像 serde、HTTP 客户端、时间处理这类常见依赖,也经常需要按 Wasm 场景开启正确 feature。

KV 只是入口,不是终点

这篇用 KV 是因为它最容易理解,也最适合做第一个 demo。实际项目里,workers-rs 还可以接入更多 Workers 绑定:

  • R2:对象存储,适合文件、图片和静态资源。
  • D1:SQLite 风格的关系型数据库。
  • Durable Objects:适合有状态协调、房间模型、实时协作。
  • Queues:适合异步任务和削峰处理。
  • Workers AI:适合在边缘侧接入 AI 能力。

不同绑定的 Rust API、feature 和配置细节不完全一样。接入前建议先看 workers-rs 仓库和 Cloudflare 对应文档。

Demo 的错误处理不能直接搬去线上

为了让示例清楚,本文代码的错误处理很轻。真正对外提供 API 时,至少建议补上:

  • 统一的 JSON 错误响应。
  • 区分 JSON 格式错误、参数缺失、KV 读写失败和配置错误。
  • POST body 增加大小限制、字段长度限制和字符校验。
  • 对公开接口增加鉴权、限流或来源校验。

边缘应用离用户近,也离异常流量近。API 边界从第一版就收紧,后面会少很多麻烦。

总结

用 workers-rs 写 Cloudflare Workers,核心流程可以压缩成六步:

  1. 准备 Rust、Wasm target、cargo-generate 和 Wrangler。
  2. cargo generate cloudflare/workers-rs 创建项目。
  3. src/lib.rs 里用 #[event(fetch)] 写请求入口。
  4. Router 组织 HTTP 路由。
  5. 在 Wrangler 配置里声明绑定,再通过 ctx.kv("...") 等 API 访问。
  6. npx wrangler dev 本地调试,用 npx wrangler deploy 部署。

如果你的项目已经重度使用 Rust,workers-rs 是一条很自然的边缘计算路径。它不一定比 TypeScript Worker 更适合所有场景,但在类型约束、系统逻辑复用和 Wasm 部署这几件事上,确实有自己的优势。

参考资料

Mttao

Mttao GitHub ↗

探索技术与生活的智慧

相关文章

/ 评论