Re0: 从零开始的 Rust For Cloudflare Workers 教程 - 文档共建 - LINUX DO

同步来源:树洞剪藏 源站剪藏 ID:85 原文地址:https://linux.do/t/topic/208550
Re0: 从零开始的 Rust For Cloudflare Workers 教程 - 文档共建 - LINUX DO
跳到主要内容
话题
我的帖子
关于
友链
文档
更多
外部链接
Status
Connect
Channel
Telegram
Shared Chat
类别
开发调优
资源荟萃
文档共建
跳蚤市场
前沿快讯
福利羊毛
搞七捻三
运营反馈
深海幽域
所有类别
标签
抽奖
公告
精华神帖
快问快答
人工智能
所有标签
消息
收件箱
频道
常规频道
直接消息
聊天
真诚 、 友善 、 团结 、 专业 ,共建你我引以为荣之社区。 《常见问题解答》
Re0: 从零开始的 Rust For Cloudflare Workers 教程
文档共建
9月 17 日
1 / 24
9月 17 日
返回
9月 24 日
前排围观支持一下 感谢分享大佬厉害啊 有点厉害支持~~
GenshinMinecraft
1 9月 17 日 #1
Re0: 从零开始的 Rust For Cloudflare Workers 教程
本文由 High Ping Network 的小伙伴 GenshinMinecraft 进行编撰,首发于 本博客 10
前言
很久很久以前,我就已经想当一位 Workers 开发者了
可惜苦于不会 JavaScript 与 TypeScript,因为用 Workers 开学也不咋好,所以一直搁置着
忽然想起 Cloudflare 官方已经支持我最爱的 Rust 了,但互联网上相关内容似乎不多,遂写本文
首先你要有这么些东西:
Rust 开发环境 (最新的 Stable)
Node.js 开发环境 (20 的 LTS)
合理的网络环境
Cloudflare 账号
一点点的 Rust 基础
本文会以一个 网易云音乐 直链 API 为教程内容,基本讲解 Rust On Cloudflare Workers 的使用
初始化项目
首先安装一下 Rust Wasm 的 Toolchain,用于编译 Wasm
rustup target add wasm32-unknown-unknown
顺便安装下 cargo-generate :
cargo install cargo-generate
随后根据 workers-rs 的模板创建项目
cargo generate cloudflare/workers-rs
如果它提问 🤷 Which template should be expanded? ,用上下箭选择 templates/hello-world 即可
随后自定义项目名称,这个项目名称会影响你部署到 Cloudflare 的项目名称以及域名。因为我们写的是 网易云音乐 API,所以我这里就取名为 netease-url
❯ cargo generate cloudflare/workers-rs
⚠️ Favorite cloudflare/workers-rs not found in config, using it as a git repository: https://github.com/cloudflare/workers-rs.git
✔ 🤷 Which template should be expanded? · templates/hello-world
🤷 Project Name: netease-url
🔧 Destination: /tmp/netease-url ...
🔧 project-name: netease-url ...
🔧 Generating template ...
🔧 Moving generated files into: /tmp/netease-url...
🔧 Initializing a fresh Git repository
✨ Done! New project created /tmp/netease-url
大概这样即可
编写代码
选择自己喜欢的编辑器就好啦!本文以 RustRover 为例
获取直链的函数
在这里,我们调用一个 API: https://oiapi.net/API/Music_163?id=NETEASEMUSICID&n=1 3
其中, NETEASEMUSICID 就是 网易云音乐的 ID,使用 GET 方法访问即可获取音乐 MP3 直链
我们使用 Reqwest 作为请求的库,需要在在 Cargo.toml 中事先添加 reqwest 和 serde_json i 的依赖,并附带上一些 features
[dependencies] ... reqwest = { version = "0.12.7" , features = [ "json" ]} serde_json = "1.0.128"
随后直接在 src/lib.rs 以正常开发的方式编写 API 调用函数即可:
pub async fn get_163_url (id: u64 ) -> std::result:: Result < String , String > { let client = Client:: new ();
let url = format! ( "https://oiapi.net/API/Music_163?id={}&n=1" , id);
let resp = match client. get (&url). send (). await { Ok (resp) => resp, Err (_) => return Err ( String :: from ( "Failed to fetch data" )), };
if !resp. status (). is_success () { return Err ( String :: from ( "Failed to fetch data" )); }
let json : Value = match resp. json (). await { Ok (json) => json, Err (_) => return Err ( String :: from ( "Failed to parse JSON" )), };
let data = match json[ "data" ]. as_array () { Some (data) => data, None => return Err ( String :: from ( "No data found" )), };
let song = match data. get ( 0 ) { Some (song) => song, None => return Err ( String :: from ( "No song found" )), };
let url = match song. get ( "url" ) { Some (url) => url. to_string (), None => return Err ( String :: from ( "No URL found" )), };
let url = &url[ 1 ..url. len ()- 1 ];
Ok (url. to_string ()) }
由于本文不是来讲解 Reqwest 是如何使用的,所以对这里的代码不会过多解释。
这个函数需要一个 id 参数,即为网易云音乐 ID,并将请求发送到 API,并从 API 中提取音乐的 URL 直链
返回值为 Result<String, String> ,T 为音乐直链,E 为错误信息
至于为什么在这里是 std::result::Result ,因为在默认引用的 workers::* 中也有 Result ,别搞混了
编写主函数
Workers 是基于路由的,所以你需要一个路由才可以响应请求,比如:
#[event(fetch)] pub async fn main (req: Request, env: Env, _ctx: Context) -> Result { let router = Router:: new (); router. run (req, env). await }
这就是主函数,主函数需要接收并处理路由, router 即为路由,我们需要在里面实现我们的业务逻辑
主页
首先来写个简单的主页,返回一个字符串:
#[event(fetch)] pub async fn main (req: Request, env: Env, ctx: Context) -> Result { let router = Router:: new (); router . get_async ( "/" , |, _| async move { Response:: ok ( "How to use: http://xxx.xxx/SongID Example: https://netease-url.genshinminecraft-d20.workers.dev/406716121" ) }) . run (req, env). await }
router 的 get_async() 需要两个参数,第一个为路径,第二个为处理的函数,我们这里使用一个闭包
该闭包不需要任何参数,所以参数中使用 _ 代替,并使用了 Response::ok() 来返回一个字符串
如果现在在该项目下执行 npx wrangler dev ,应该可以在本地的 8787 端口上查看实例:
1125×366 24.4 KB
在该模式下,一旦源代码有更新,就会自动编译并部署,可以在浏览器实时查看情况
处理歌曲请求
写完了主页,下面就是处理逻辑,这是关键代码:
let router = Router:: new (); router . get_async ( "/" , |, | async move { Response:: ok ( "How to use: http://xxx.xxx/SongID Example: https://netease-url.genshinminecraft-d20.workers.dev/406716121" ) }) . get_async ( "/:id" , |, ctx| async move { let id = match ctx. param ( "id" ) { Some (id) => match id.parse::< u64 >() { Ok (id) => id, Err () => return Response:: error ( "Invalid ID" , 400 ), }, None => return Response:: error ( "Invalid ID" , 400 ), }; let url = match get_163_url (id). await { Ok (url) => url, Err (err) => return Response:: error (err, 400 ), }; Response:: ok (url) }) . run (req, env) . await
在这里,我们新加了一个 get_async() ,路径为 /:id ,其中 id 为变量,可以接收并处理,后面闭包中的 ctx 可以用来接收路径中 id 的值
这里我们使用 ctx.param("id") 来接收 路径中 id 的值,并调用上面的函数返回错误或 URL
代码总结
最后,代码大概这样的:
use reqwest::Client; use serde_json::{json, Value}; use worker::*;
#[event(fetch)] pub async fn main (req: Request, env: Env, ctx: Context) -> Result { let router = Router:: new (); router . get_async ( "/" , |, | async move { Response:: ok ( "How to use: http://xxx.xxx/SongID Example: https://netease-url.genshinminecraft-d20.workers.dev/406716121" ) }) . get_async ( "/:id" , |, ctx| async move { let id = match ctx. param ( "id" ) { Some (id) => match id.parse::< u64 >() { Ok (id) => id, Err (_) => return Response:: error ( "Invalid ID" , 400 ), }, None => return Response:: error ( "Invalid ID" , 400 ), }; let url = match get_163_url (id). await { Ok (url) => url, Err (err) => return Response:: error (err, 400 ), }; Response:: ok (url) }) . run (req, env) . await }
pub async fn get_163_url (id: u64 ) -> std::result:: Result < String , String > { let client = Client:: new ();
let url = format! ( "https://oiapi.net/API/Music_163?id={}&n=1" , id);
let resp = match client. get (&url). send (). await { Ok (resp) => resp, Err (_) => return Err ( String :: from ( "Failed to fetch data" )), };
if !resp. status (). is_success () { return Err ( String :: from ( "Failed to fetch data" )); }
let json : Value = match resp. json (). await { Ok (json) => json, Err (_) => return Err ( String :: from ( "Failed to parse JSON" )), };
let data = match json[ "data" ]. as_array () { Some (data) => data, None => return Err ( String :: from ( "No data found" )), };
let song = match data. get ( 0 ) { Some (song) => song, None => return Err ( String :: from ( "No song found" )), };
let url = match song. get ( "url" ) { Some (url) => url. to_string (), None => return Err ( String :: from ( "No URL found" )), };
let url = &url[ 1 ..url. len ()- 1 ];
Ok (url. to_string ()) }
发布到 Cloudflare Workers
在该项目目录下执行:
npx wrangler deploy
在期间,Wrangler 可能会让你登录 Cloudflare 账户,跳转浏览器登录即可
当部署完成,你就可以在 Cloudflare Dashboard 与控制台下看见对应的 Workers 了:
⚡ Done in 7ms Total Upload: 672.52 KiB / gzip: 266.48 KiB Worker Startup Time: 1 ms Uploaded netease-url (6.98 sec) Deployed netease-url triggers (0.51 sec) https://netease-url.genshinminecraft-d20.workers.dev Current Version ID: dffa2bd9-ee72-4a1f-99aa-8251379cea93
2678×1589 292 KB
小结
就这样吧,Rust Wasm 可玩性还是很高的,Worker-rs 的 req 与 ctx 有更多的功能,终于让我这个 JS / TS 全都不会的也玩上 Workers 了
欢迎加入 High Ping 大家庭:
官网 8
Blog 10
@HighPingNetwork 1
@HighPingChat 1
heart
+1
38
编辑
963
浏览量
43
赞
6
链接
14
用户
7
4
2
阅读时间
4 分钟
卡尔 · 马克思 pengzhile 大预言家
9月 17 日 #2
感谢分享,很有帮助
1 回复
0x24a 活跃用户
9月 17 日 #3
赞赞赞,对于我这种对 JS 一窍不通的很有用
1 回复
opus
9月 17 日 #4
都会 rust 了,js 这玩意儿不是分分钟上手么 1 个回复
回复
GenshinMinecraft
opus 9月 17 日 #5
还真不是,js 只是不想学 1 个回复
2 回复
opus GenshinMinecraft 9月 17 日 #6
那倒是,worker 这种场景无所谓技术选型,怎么顺手怎么来
回复
大帅哥 handsome 种子用户
9月 17 日 #7
感谢大佬教程
回复
0wl
9月 17 日 #8
期待一步到位 windows for worker,连买机的钱都省了 1 个回复
回复
上次访问
torlee 种子用户
9月 18 日 #9
感谢分享,厉害啊。
回复
GenshinMinecraft
0wl 9月 18 日 #10
不太可能啦,workers 还是只能执行一次的
回复
aric
9月 18 日 #11
支持技术贴,大佬牛
回复
Jens someway
9月 23 日 #12
可以自由使用第三方的 Package 吗?python 目前只支持一部分 stdlib 的 package, 第三方比如 fastapi, langchain 啥的都只支持本地运行,一点意义都没有。 2 个回复
回复
GenshinMinecraft
Jens 9月 23 日 #13
我没有太关注 但是他是编译型的 至少 reqwest serde 是可以用的
回复
lyyyyy lyyzzz1 大预言家
9月 23 日 #14
感谢佬的教程,最近刚好因为实习原因要学一学 rust,又有了一点动力
回复
Scheme Matrix 活跃用户
1 Jens 9月 23 日 #15
参见:
Cloudflare Docs
Supported crates | Cloudflare Workers docs
Learn about popular Rust crates which have been confirmed to work on Workers when using workers-rs ↗ (or in some cases just wasm-bindgen), to write Workers in WebAssembly. Each Rust crate example includes any custom configuration that is required.
简单来说,使用的 crate 需要支持 wasm32-unknown-unknown target。
Python 的情况下,或许编译到 wasm 是一种解决方案。不过,Wokers 的大小限制可能是一个问题:
A Worker can be up to 10 MB in size after compression on the Workers Paid plan, and up to 1 MB on the Workers Free plan.
Cloudflare Docs
Limits | Cloudflare Workers docs
Cloudflare Workers plan and platform limits.
编辑:
有项目在 Go 中实现了类似的东西。
github.com
GitHub - syumai/workers: Go package to run an HTTP server on Cloudflare... 1
Go package to run an HTTP server on Cloudflare Workers.
1 个回复
1 回复
黑神话·悟空 Haven 大预言家
9月 23 日 #16
大佬们是不屑 JS 吧 JS 多简单呀
回复
GenshinMinecraft
Scheme Matrix 9月 23 日 #17
实际上并不止 我也不太了解他的限定 1 个回复
回复
Scheme Matrix 活跃用户 GenshinMinecraft 9月 23 日 #18
GenshinMinecraft:
实际上并不止
有例子吗?从原理上看,Cloudflare Workers 平台上的 Rust 无论如何应该是 WebAssembly…… 1 个回复
回复
GenshinMinecraft
Scheme Matrix 9月 23 日 #19
wasm32-unknown-unknown 的支持是必须要有的 但是其他的只要你能装上并且编译通过就没什么问题 1 个回复
回复
Scheme Matrix 活跃用户 GenshinMinecraft 9月 23 日 #20
但以我的有限经验来看,在这种前提下,如果依赖链中某个 crate 明确不支持 wasm32-unknown-unknown 目标,编译 应该 会失败…… 1 个回复
回复
开始阅读 启用自动点赞
楼主
等级