← レッスン一覧に戻る レッスン 6 / 8
中級 api
Fetch APIコール
はじめに
Rust/Wasmは web-sys を通じてブラウザのネイティブFetch APIを使ったHTTPリクエストが可能です。wasm-bindgen-futures と組み合わせることで、完全なasync/awaitサポートが得られます — RustのFutureはJavaScriptのPromiseに自動的にブリッジされます。
必要な依存関係
[dependencies]
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
js-sys = "0.3"
[dependencies.web-sys]
version = "0.3"
features = [
"Window",
"Request",
"RequestInit",
"RequestMode",
"Response",
"Headers",
]Wasmでの非同期処理の仕組み
Wasmはシングルスレッドで、ブラウザのメインスレッド上で動作します。Wasmでの非同期処理は以下のように機能します:
wasm-bindgen-futuresがRustのFutureとJSのPromiseを相互変換.awaitした時点で制御がJSイベントループに戻る- Promiseが解決されると、Rustコードが再開される
// このRust非同期関数は...
#[wasm_bindgen]
pub async fn do_work() -> Result<JsValue, JsValue> {
let result = some_async_operation().await?;
Ok(result)
}
// ...JavaScriptではこうなる:
// const result = await do_work(); // Promiseを返すGETリクエスト
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::JsFuture;
use web_sys::{Request, RequestInit, Response, window};
#[wasm_bindgen]
pub async fn fetch_json(url: &str) -> Result<JsValue, JsValue> {
let mut opts = RequestInit::new();
opts.method("GET");
let request = Request::new_with_str_and_init(url, &opts)?;
let window = window().unwrap();
let resp: Response = JsFuture::from(
window.fetch_with_request(&request)
).await?.dyn_into()?;
let json = JsFuture::from(resp.json()?).await?;
Ok(json)
}JSONボディ付きPOSTリクエスト
use js_sys::JSON;
#[wasm_bindgen]
pub async fn post_json(url: &str, body: JsValue) -> Result<JsValue, JsValue> {
let mut opts = RequestInit::new();
opts.method("POST");
opts.body(Some(&JSON::stringify(&body)?));
let request = Request::new_with_str_and_init(url, &opts)?;
request.headers().set("Content-Type", "application/json")?;
let window = window().unwrap();
let resp: Response = JsFuture::from(
window.fetch_with_request(&request)
).await?.dyn_into()?;
let json = JsFuture::from(resp.json()?).await?;
Ok(json)
}JavaScriptからの呼び出し
import init, { fetch_json, post_json } from './pkg/my_wasm.js';
await init();
// GET
const data = await fetch_json("https://api.example.com/items");
console.log(data);
// POST
const result = await post_json("https://api.example.com/items", {
name: "New Item",
value: 42
});エラーハンドリング
resp.ok() を必ず確認しましょう — Fetch APIはHTTPエラー(4xx/5xx)でもスローしません:
let resp: Response = /* ... */;
if !resp.ok() {
let status = resp.status();
let text = JsFuture::from(resp.text()?).await?;
return Err(JsValue::from_str(
&format!("HTTP {}: {}", status, text.as_string().unwrap_or_default())
));
}セキュリティ:CORS
Wasmからのすべてのfetchリクエストは、ブラウザのCORS(Cross-Origin Resource Sharing)ポリシーに従います:
- 同一オリジンのリクエストは常に成功
- クロスオリジンのリクエストはサーバーが
Access-Control-Allow-Originヘッダーを送信する必要がある - リクエストモードを明示的に設定できます:
opts.mode(web_sys::RequestMode::Cors); // デフォルト
opts.mode(web_sys::RequestMode::NoCors); // 不透明なレスポンスWasmモジュールはホスティングページと同じオリジン制限で動作します — 特別な権限はありません。
試してみよう
JSONリクエストボディ付きのPOSTサポートを追加してみましょう。
試してみる
チャプタークイズ
すべての問題に正解してレッスンを完了しましょう