如何在Rust的wasm_bindgen函数中实现可通过wasm-pack编译的同步HTTP请求?
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
reqwest = "0.9"
### 打包命令 ```bash wasm-pack build --release --target nodejs
错误信息
error: could not compile `net2`
我发现net2似乎不被wasm-pack支持,所以没法在当前场景用reqwest。请问有没有能成功通过wasm-pack打包的同步HTTP请求实现方案?
解答
哦,这个问题我太熟悉了!你遇到的核心问题有两个:
reqwest0.9的同步API依赖原生Rust的网络库(比如net2),这些库完全不支持WASM编译目标,所以打包时会报错;- 在WASM环境(包括Node.js目标的WASM)里,根本不存在真正的同步HTTP请求——因为底层的网络API(浏览器
fetch/Node.jshttp)全是异步的,WASM本身也没有阻塞式网络的能力。测试能跑是因为测试是在原生Rust环境执行的,不是WASM。
不过别担心,有两种成熟的异步替代方案,这也是WASM网络请求的标准做法:
方案1:用reqwest异步版 + wasm-bindgen-futures
升级reqwest到支持WASM的版本(0.11+开始官方支持WASM),配合wasm-bindgen-futures把异步Rust代码封装成JS的Promise:
1. 更新Cargo.toml
[lib] crate-type = ["cdylib"] [dependencies] wasm-bindgen = "0.2" wasm-bindgen-futures = "0.4" reqwest = { version = "0.11", features = ["wasm-bindgen"] }
2. 修改Rust代码
把同步函数改成异步的,返回Result<String, JsValue>(会自动转换成JS的Promise):
#![allow(non_snake_case)] use wasm_bindgen::prelude::*; use reqwest::Client; #[wasm_bindgen] pub async fn makeGetRequest(url: &str) -> Result<String, JsValue> { let client = Client::new(); // 发送请求并处理错误(转换成JS可识别的错误) let resp = client.get(url) .send() .await .map_err(|e| JsValue::from_str(&e.to_string()))?; // 读取响应文本 let body = resp.text() .await .map_err(|e| JsValue::from_str(&e.to_string()))?; Ok(body) } #[cfg(test)] mod tests { use super::*; // 测试需要异步运行时,用tokio use tokio::test; #[test] async fn test_makeGetRequest() { let url = "https://stackoverflow.com/"; let body = makeGetRequest(url).await.unwrap(); assert!(body.len() > 0); } }
3. Node.js中调用
打包后,在JS里用await调用即可:
const { makeGetRequest } = require('./pkg/your_package_name'); async function run() { try { const body = await makeGetRequest('https://stackoverflow.com/'); console.log(`响应长度:${body.length}`); } catch (err) { console.error('请求失败:', err); } } run();
方案2:直接绑定JS的fetch API
如果不想依赖reqwest,可以直接通过web-sys绑定浏览器/Node.js的fetch API,更轻量:
1. 更新Cargo.toml
[lib] crate-type = ["cdylib"] [dependencies] wasm-bindgen = "0.2" wasm-bindgen-futures = "0.4" web-sys = { version = "0.3", features = ["Request", "RequestInit", "RequestMode", "Response", "Window"] }
2. 修改Rust代码
#![allow(non_snake_case)] use wasm_bindgen::prelude::*; use wasm_bindgen_futures::JsFuture; use web_sys::{Request, RequestInit, RequestMode, Response}; #[wasm_bindgen] pub async fn makeGetRequest(url: &str) -> Result<String, JsValue> { // 配置请求参数 let mut opts = RequestInit::new(); opts.method("GET"); opts.mode(RequestMode::Cors); let request = Request::new_with_str_and_init(url, &opts)?; // 获取全局window对象(Node.js环境下也兼容) let window = web_sys::window().ok_or("无法获取全局window对象")?; let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; // 转换为Response对象并检查状态 let resp: Response = resp_value.dyn_into()?; if !resp.ok() { return Err(JsValue::from_str(&format!("请求失败,状态码:{}", resp.status()))); } // 读取响应文本 let text = JsFuture::from(resp.text()?).await?; Ok(text.as_string().unwrap_or_default()) } #[cfg(test)] mod tests { use super::*; use wasm_bindgen_test::*; #[wasm_bindgen_test] async fn test_makeGetRequest() { let url = "https://stackoverflow.com/"; let body = makeGetRequest(url).await.unwrap(); assert!(body.len() > 0); } }
关于“同步请求”的补充
如果你确实需要在代码里看起来像同步调用,在Node.js中只能用await异步函数——这是唯一可行的方式。强行模拟同步请求会导致Node.js事件循环卡死,完全不推荐。
内容的提问来源于stack exchange,提问作者Fomalhaut
相关产品推荐
相关产品推荐

