You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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请求实现方案?


解答

哦,这个问题我太熟悉了!你遇到的核心问题有两个:

  1. reqwest 0.9的同步API依赖原生Rust的网络库(比如net2),这些库完全不支持WASM编译目标,所以打包时会报错;
  2. 在WASM环境(包括Node.js目标的WASM)里,根本不存在真正的同步HTTP请求——因为底层的网络API(浏览器fetch/Node.js http)全是异步的,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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 09:22:50