reqwest

【Rust】reqwest の基本と使い方を分かりやすく解説

【Rust】reqwest の基本と使い方を分かりやすく解説
naoki-hn

Rust で HTTP 通信を行うためのクレートである reqwest の基本的な使い方について、初心者にも分かりやすく解説します。

reqwest クレートの概要

reqwest とは

reqwest は、Rust で HTTP 通信を行うための代表的なクレートです。GET や POST などの HTTP リクエストを簡潔なコードで記述でき、JSON の送受信、認証ヘッダーの付与、タイムアウト設定なども柔軟に対応しています。

Rust の標準ライブラリには、HTTP クライアント機能は含まれていないため、Web API との通信が必要な場面では、reqwest がデファクトスタンダードとして広く使用されています。

この記事では、reqwest の基本的な使い方を紹介します。

非同期モードと同期モード

reqwest には、非同期モードと同期モードの 2 種類があります。reqwest は、非同期処理ランタイムの tokio をベースに構築されており、デフォルトは非同期モードです。

  • 非同期モード(デフォルト):async / await を使用して非同期でリクエストを送信します。非同期処理ランタイムである tokio が必要です。
  • 同期モード:reqwest::blocking モジュールを使うことで、通常の同期コードとして HTTP リクエストを書くことができます。使用する場合には、blocking feature を有効にする必要があります。

この記事では、実務上よく使われる非同期モードを中心に解説します。同期モード(reqwest::blocking)については、後半で簡単に触れます。

reqwest を使用した HTTP 通信の基本

reqwest の導入方法

reqwest は、他のクレートと同様に cargo add または Cargo.toml に追記することで利用できます。

cargo add で追加する場合

cargo add reqwest --features json,query
cargo add tokio --features full

Cargo.toml に追記する場合

[dependencies]
reqwest = { version = "0.13", features = ["json", "query"] }
tokio = { version = "1", features = ["full"] }

feature について

reqwest の json feature を有効にすることで、json メソッドが使えるようになります。これにより、リクエストボディへの JSON 埋め込みや、レスポンスの JSON デシリアライズを簡単に行うことができます。JSON を扱う場合には、必ず指定するようにしましょう。また、以降で使用する query メソッドも使えるように query feature も有効にします。

tokio の full feature は、tokio が提供するすべての機能を有効にしておきます。

導入時には crates.io の reqwesttokio のページを確認してください。なお、実際にインストールされたバージョンは Cargo.lock ファイルで確認できます。

クレートや依存関係の基本については以下を参考にしてください。

クレート(バイナリ/ライブラリ)と依存関係の管理を分かりやすく解説

テスト用サービス httpbin.org

HTTP リクエストとレスポンスをテストする

HTTP リクエストとレスポンスのテストをするためには、API を提供するサーバーが必要になります。今回は「httpbin.org」を使用します。

httpbin.org は、HTTP リクエストとレスポンスのテストに使用される人気のオンラインサービスで、開発者が HTTP リクエストを送信し、レスポンスを確認することができます。

httpbin.org は、WebAPI 開発や HTTP クライアントのライブラリ、フレームワークのテストなどに広く使用されています。

Docker 環境の利用

httpbin.org は、通信タイミングによってサービスが不安定なことがあります。httpbin.org のサービスは、Docker 環境が用意されているため、Docker 環境が手元にある場合には、以下のコマンドでローカル実行してテストする方が安定的に動作の確認が可能です。

docker run --rm -p 8080:80 kennethreitz/httpbin

上記コマンドを実行すると「kennethreitz/httpbin」というコンテナ環境をインストールし、起動します。--rm オプションは、終了時にコンテナを削除するコマンドで、-p 8080:80 はポートの接続設定です。ローカル環境からは 8080 ポートを使用してコンテナにアクセスします。

なお、httpbin.org にアクセスする場合は、https で SSL可能ですが、ローカルの Docker 環境は SSL 通信は対応していないので http でアクセスする必要があります。

以降のサンプルコードでは、https://httpbin.org への URL を記載しています。ローカルの Docker 環境を使う場合は http://localhost:8080 に切り替えて試してください。

各 HTTP メソッドでリクエストを送信する
GET / POST/ PUT / DELETE

基本的な使い方の流れ

reqwest を用いて HTTP 通信をする際の基本的な流れは以下の通りです。

  1. ClientBuilder を生成して、タイムアウトなどの通信の設定をする。
  2. ClientBuilder から Client を生成する。
  3. RequestBuilder を生成し、パラメータなどの設定をする。
  4. リクエストを送信 (send) し、レスポンスを取得する。
  5. 取得したレスポンスからヘッダーやボディなど必要な情報を取り出す。

以降では、上記の流れで各 HTTP メソッドを使用する例を順番に紹介します。

GET リクエストでレスポンスを取得する

基本的な使い方

基本的な GET リクエストを送ってレスポンスを取得する例から見ていきましょう。

use std::error::Error;
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    // httpbin.org から取得する
    let url = "https://httpbin.org/get";
    // Docker を使用する場合は、以下のように URL を変更してください
    // let url = "http://localhost:8080/get";

    // ClientBuilder でタイムアウトなどを設定する
    let client_builder = reqwest::Client::builder().timeout(Duration::from_secs(10));

    // ClientBuilder から Client を作成する
    let client = client_builder.build()?;

    // RequestBuilder を作成する
    let request_builder = client.get(url);

    // レスポンスを取得する
    let response = request_builder.send().await?;

    // ステータスコードを取得する
    let status = response.status();
    println!("ステータスコード:\n{status}\n");

    // レスポンスボディを文字列として取得する
    let text = response.text().await?;
    println!("応答の文字列形式:\n{text}");

    Ok(())
}
【実行結果】
ステータスコード:
200 OK

応答の文字列形式:
{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Host": "httpbin.org", 
    "X-Amzn-Trace-Id": "Root=1-6a49b680-6c650d0050fcf1c707d6e742"
  }, 
  "origin": "xxx.xxx.xxx.xxx", 
  "url": "https://httpbin.org/get"
}

#[tokio::main] は、main 関数を非同期関数として実行するためのものです。reqwest の非同期モードでは、tokio のランタイムが必要なため、この指定が必要です。

まず、reqwest::Client::builder()ClientBuilder を生成し、timeout() で通信タイムアウトを設定しています。

次に、client.get(url) で、RequestBuilder を生成し、send().await でリクエストを送信します。このとき、await で非同期処理の完了を待ち、? 演算子でエラーを呼び出し元に伝播しています。

レスポンスからは、status() でステータスコードを取得でき、ボディの文字列を取得する際には、text().await を使用します。text() は非同期処理のため、await が必要な点に注意してください。

クエリパラメータを指定する

GET リクエストを送る際に、検索の条件となるクエリパラメータを指定する場合には、query メソッドを使用します。

use std::error::Error;
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    ...(Client 作成までは同様なので省略)...

    // RequestBuilder を作成する
    // query() でクエリパラメータを付与することができる
    let queries = [("key1", "value1"), ("key2", "value2")];
    let request_builder = client.get(url).query(&queries);

    // レスポンスを取得する
    let response = request_builder.send().await?;

    ...(ステータスコード表示とボディの表示は同様なので省略)...
}
【実行結果】
ステータスコード:
200 OK

応答の文字列形式:
{
  "args": {
    "key1": "value1", 
    "key2": "value2"
  }, 
  "headers": {
    "Accept": "*/*", 
    "Host": "httpbin.org", 
    "X-Amzn-Trace-Id": "Root=1-6a49b7d7-169234f062f0d8bb46cea5c2"
  }, 
  "origin": "xxx.xxx.xxx.xxx", 
  "url": "https://httpbin.org/get?key1=value1&key2=value2"
}

query メソッドにキーと値を持った配列を渡すことで、クエリパラメータを設定できます。具体的な URL としては、https://httpbin.org/get?key1=value1&key2=value2 のように送信されることになります。

ヘッダーを扱う

リクエストの際にはヘッダー情報を指定する場合や、レスポンスのヘッダーを確認することがよくあります。ヘッダーの扱いについても以下で確認しましょう。

use std::error::Error;
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    ...(Client 作成までは同様なので省略)...

    // RequestBuilder を作成する
    // header() でリクエストヘッダーを付与することができる
    // API キーを送信する場合によく使われる例
    let request_builder = client.get(url).header("X-API-Key", "sample-api-key");

    // レスポンスを取得する
    let response = request_builder.send().await?;

    // ステータスコードを取得する
    let status = response.status();
    println!("ステータスコード:\n{status}\n");

    // ヘッダーを取得する
    let headers = response.headers();
    println!("ヘッダー:\n{headers:?}\n");

    // ヘッダーから特定の項目だけを取り出す例
    // ヘッダーは HeaderMap 型で取得されるので、get() メソッドで取り出す
    // 値の型は HeaderValue なので、文字列に変換する必要がある
    let content_type = headers
        .get("content-type")
        .ok_or("content-type ヘッダーが見つかりません")?
        .to_str()?;
    println!("content-type ヘッダー: {content_type}\n");

    // レスポンスボディを文字列として取得する
    let text = response.text().await?;
    println!("応答の文字列形式:\n{text}");

    Ok(())
}
【実行結果】
ステータスコード:
200 OK

ヘッダー:
{"date": "Sun, 05 Jul 2026 02:06:48 GMT", "content-type": "application/json", "content-length": "257", "server": "gunicorn/19.9.0", "access-control-allow-origin": "*", "access-control-allow-credentials": "true"}

content-type ヘッダー: application/json

応答の文字列形式:
{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Host": "httpbin.org", 
    "X-Amzn-Trace-Id": "Root=1-6a49bc38-646f78d43467315669dd0b4a", 
    "X-Api-Key": "sample-api-key"
  }, 
  "origin": "xxx.xxx.xxx.xxx", 
  "url": "https://httpbin.org/get"
}

リクエストのヘッダーに固有のヘッダを付与したい場合には、RequestBuilder で、header メソッドを使用します。例の「header("X-API-Key", "sample-api-key")」のような指定は、外部サービスの REST API を使用する際に固有の認証キーを使う際によく利用されます。

レスポンスのヘッダー情報は、headers メソッドで全体を取得できます。ヘッダーは、HeaderMap 型であり、特定の項目を取り出す際は、get() でアクセスします。取得される値は HeaderValue 型のため、最後に to_str で文字列に変換します。

JSON レスポンスを構造体にデシリアライズする

Web API では JSON 形式のレスポンスを受け取るケースが多くあります。reqwest では serde と組み合わせることで、JSON レスポンスを構造体に直接デシリアライズできます。デシリアライズするためには serde が必要です。cargo addで追加しておいてください。

cargo add serde --features derive
use serde::Deserialize;
use std::collections::HashMap;
use std::error::Error;
use std::time::Duration;

// url と headers だけを保持する構造体
#[derive(Debug, Deserialize)]
struct HttpBinResponse {
    url: String,
    headers: HashMap<String, String>,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    ...(Client 作成までは同様なので省略)...

    // RequestBuilder を作成する
    let request_builder = client.get(url);

    // レスポンスを取得する
    let response = request_builder.send().await?;

    // json() で構造体にデシリアライズする
    let data: HttpBinResponse = response.json().await?;

    // url と headers を取り出す
    println!("url: {}", data.url);
    println!("headers: {:?}", data.headers);

    Ok(())
}
【実行結果】
url: https://httpbin.org/get
headers: {"X-Amzn-Trace-Id": "Root=1-6a498cbb-25fe1cde73cbb10827f0d817", "Accept": "*/*", "Host": "httpbin.org"}

json().await で、レスポンスボディを指定した型に直接デシリアライズできます。受け取る変数には「let data: HttpBinResponse」のように型を明示する必要があります。また、JSON で取り込む構造体は、#[derive(Deserialize)]を付けて定義する必要があります。

JSON のフィールドと構造体のフィールドが一致しない場合や、型が合わない場合にはエラーとなります。存在しない場合があるフィールドは Option<T> にしておくと安全です。

serde を使った JSON の扱いについては「serde で JSON を扱う基本を分かりやすく解説」を参考にしてください。

POST リクエストで JSON を送信する

JSON データを含めて POST リクエストを送信する例を見てみましょう。POST でデータを登録する場合には、対象サービスのエンドポイントが用意されます。

今回は、https://httpbin.org/anything/users というエンドポイントがユーザー登録のためのエンドポイントとして例を紹介します。なお、httpbin.org で /anything/anything/<任意パス> は、HTTPメソッドを問わず、リクエスト内容をそのまま返却するようなエンドポイントです。今回は、users というユーザー登録のエンドポイント例の代替として使用します。

use std::error::Error;
use std::time::Duration;

use serde::Serialize;

// リクエストボディとして送信するデータ
// user_id はサーバー側で採番される想定とする
#[derive(Serialize)]
struct User {
    name: String,
    age: u32,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    // httpbin.org へ送信する
    // ユーザー登録エンドポイントを呼び出す
    let url = "https://httpbin.org/anything/users";
    // Docker を使用する場合は、以下のように URL を変更してください
    // let url = "http://localhost:8080/anything/users";

    // ClientBuilder でタイムアウトなどを設定する
    let client_builder = reqwest::Client::builder().timeout(Duration::from_secs(10));

    // ClientBuilder から Client を作成する
    let client = client_builder.build()?;

    // 送信するデータ
    let user = User {
        name: "Taro".to_string(),
        age: 30,
    };

    // RequestBuilder を作成する
    // json() でリクエストボディを JSON として送信する
    let request_builder = client.post(url).json(&user);

    // レスポンスを取得する
    let response = request_builder.send().await?;

    // ステータスコードを取得する
    let status = response.status();
    println!("ステータスコード:\n{status}\n");

    // レスポンスボディを文字列として取得する
    let text = response.text().await?;
    println!("応答の文字列形式:\n{text}");

    Ok(())
}
【実行結果】
ステータスコード:
200 OK

応答の文字列形式:
{
  "args": {}, 
  "data": "{\"name\":\"Taro\",\"age\":30}", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Content-Length": "24", 
    "Content-Type": "application/json", 
    "Host": "httpbin.org", 
    "X-Amzn-Trace-Id": "Root=1-6a49c912-2d1d37541f89dcbd32a31524"
  }, 
  "json": {
    "age": 30, 
    "name": "Taro"
  }, 
  "method": "POST", 
  "origin": "xxx.xxx.xx.xxx", 
  "url": "https://httpbin.org/anything/users"
}

client.post(url) で POST リクエストの RequestBuilder を作成し、json(&user) で送信したい構造体のデータを設定します。

送信するデータの構造体には #[derive(Serialize)] を付与しておく必要があります。なお、json() は内部で Content-Type: application/json ヘッダーも自動付与してくれます。

PUT リクエストでデータを更新する

PUT リクエストについても考えましょう。今回の例では、既に user_id が POST により登録されており、https://httpbin.org/anything/users/u1001 というユーザー ID のエンドポイントを PUT で呼び出すと考えます。

REST API では、このようにエンドポイントで対象となる ID をパスパラメータとして指定できるように作られることが一般的です。

use std::error::Error;
use std::time::Duration;

use serde::Serialize;

// リクエストボディとして送信するデータ
// user_id は URL のパスパラメータで指定するため、ボディには含めない
#[derive(Serialize)]
struct User {
    name: String,
    age: u32,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    // 更新対象の user_id
    let user_id = "u1001";

    // httpbin.org へ送信する
    // user_id をパスパラメータとして URL に含める
    let url = format!("https://httpbin.org/anything/users/{user_id}");
    // Docker を使用する場合は、以下のように URL を変更してください
    // let url = format!("http://localhost:8080/anything/users/{user_id}");

    ...(Client 作成までは同様なので省略)...

    // 更新後のデータ
    let user = User {
        name: "Miki".to_string(),
        age: 25,
    };

    // RequestBuilder を作成する
    // put() で PUT リクエストを送信する
    let request_builder = client.put(url).json(&user);

    // レスポンスを取得する
    let response = request_builder.send().await?;

    // ステータスコードを取得する
    let status = response.status();
    println!("ステータスコード:\n{status}\n");

    // レスポンスボディを文字列として取得する
    let text = response.text().await?;
    println!("応答の文字列形式:\n{text}");

    Ok(())
}
【実行結果】
ステータスコード:
200 OK

応答の文字列形式:
{
  "args": {}, 
  "data": "{\"name\":\"Miki\",\"age\":25}", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Content-Length": "24", 
    "Content-Type": "application/json", 
    "Host": "httpbin.org", 
    "X-Amzn-Trace-Id": "Root=1-6a49c11f-39b3368462de6e1f2244052c"
  }, 
  "json": {
    "age": 25, 
    "name": "Miki"
  }, 
  "method": "PUT", 
  "origin": "xxx.xxx.xxx.xxx", 
  "url": "https://httpbin.org/anything/users/u1001"
}

対象とする ID 情報は、例のように format! を用いて埋め込むのが簡単です。それ以降の処理の流れは、postput に変わるだけで同様の流れとなります。

DELETE リクエストでデータを削除する

DELETE リクエストも、PUT と同様に https://httpbin.org/anything/users/u1001 に対して DELETE のリクエストを送信する例を考えます。

use std::error::Error;
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    // 削除対象の user_id
    let user_id = "u1001";

    // httpbin.org へ送信する
    // user_id をパスパラメータとして URL に含める
    let url = format!("https://httpbin.org/anything/users/{user_id}");
    // Docker を使用する場合は、以下のように URL を変更してください
    // let url = format!("http://localhost:8080/anything/users/{user_id}");

    ...(Client 作成までは同様なので省略)...

    // RequestBuilder を作成する
    // delete() で DELETE リクエストを送信する
    let request_builder = client.delete(url);

    // レスポンスを取得する
    let response = request_builder.send().await?;

    // ステータスコードを取得する
    let status = response.status();
    println!("ステータスコード:\n{status}\n");

    // レスポンスボディを文字列として取得する
    let text = response.text().await?;
    println!("応答の文字列形式:\n{text}");

    Ok(())
}
【実行結果】
ステータスコード:
200 OK

応答の文字列形式:
{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Host": "httpbin.org", 
    "X-Amzn-Trace-Id": "Root=1-6a49c102-51e1f4260751237d2a3ea3c7"
  }, 
  "json": null, 
  "method": "DELETE", 
  "origin": "xxx.xxx.xxx.xxx", 
  "url": "https://httpbin.org/anything/users/u1001"
}

DELETE の場合は、対象が識別できればいいので、エンドポイントを指定して delete メソッドを呼び出しているだけになります。プログラムの構成としては、PUT と同様です。

注意点

上記はあくまで、想定の API の呼び出し例です。実際には、各 HTTP メソッドでの呼び出し方は各サービスの仕様に依存します。対象とする API の仕様をしっかりと確認して使い方を考えるようにしてください。

エラー情報に対処する

各 HTTP メソッドの使い方を見てきました。これまでの例は、成功ステータスの例のみでしたが、実開発では、エラーステータスコードの場合、適切な処理が必要です。4xx5xx 番はエラーとして扱いますが、これまで紹介したメソッドは、レスポンスが返ってくれば 4xx5xx でも Ok 扱いとなります。

そのため、エラーステータスを判断して処理をしたい場合は、レスポンスの error_for_status メソッドを使用します。このメソッドは、4xx5xx の場合に、Err を返すため、? 演算子と組み合わせるなど状況に応じて適切にエラー処理ができます。

httpbin.org には、特定のエラーコードを返却するエンドポイントが用意されているので、今回は、ステータスコード 400 が返ってくる場合の対処例を紹介します。

use std::error::Error;
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    // httpbin.org から取得する
    let url = "https://httpbin.org/status/400";
    // Docker を使用する場合は、以下のように URL を変更してください
    // let url = "http://localhost:8080/status/400";

    // ClientBuilder でタイムアウトなどを設定する
    let client_builder = reqwest::Client::builder().timeout(Duration::from_secs(10));

    // ClientBuilder から Client を作成する
    let client = client_builder.build()?;

    // ケース1: HTTP レベルのエラー(ステータスコードが 4xx や 5xx)
    // 通信自体は成功しているため、send() は Ok(Response) を返す
    let response = client.get(url).send().await?;
    println!("ステータスコード: {}\n", response.status());

    // error_for_status() を使うと、4xx・5xx のステータスコードを Err に変換できる
    match response.error_for_status() {
        Ok(response) => println!("成功として扱われました: {}", response.status()),
        Err(error) => println!("error_for_status() でエラーになりました: {error}"),
    }
    println!();

    // ケース2: 通信レベルのエラー
    // 存在しないホスト名のため、DNS 解決に失敗して send() 自体が Err を返す
    let invalid_url = "https://this-host-does-not-exist.com";
    match client.get(invalid_url).send().await {
        Ok(response) => println!("ステータスコード: {}", response.status()),
        Err(error) => println!("send() でエラーになりました: {error}"),
    }

    Ok(())
}
【実行結果】
ステータスコード: 400 Bad Request

error_for_status() でエラーになりました: HTTP status client error (400 Bad Request) for url (https://httpbin.org/status/400)

send() でエラーになりました: error sending request for url (https://this-host-does-not-exist.com/)

ケース1 は、通信は通っているものの適切なリクエストの仕方ではなかったため、400 番のレスポンスがあった場合です。一方で、ケース2 の方は、そもそも通信レベルのエラーの場合です。例では存在しない URL へアクセスしようとして通信が返ってきません。この時は、send の時点で Err が返ってきます。どちらのケースのエラーであるのかを判断できるようにコーディングするようにしましょう。

汎用メソッド request を使用する

これまで、getpostputdelete と各 HTTP メソッドごとの専用メソッドを使ってきました。reqwest には、HTTP メソッドを動的に指定できる request という汎用メソッドも用意されています。

use std::error::Error;
use std::time::Duration;

use reqwest::Method;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    // ClientBuilder でタイムアウトなどを設定する
    let client_builder = reqwest::Client::builder().timeout(Duration::from_secs(10));

    // ClientBuilder から Client を作成する
    let client = client_builder.build()?;

    // 対象の user_id
    let user_id = "u1001";

    // request() を使うと、get()・post()・put()・delete() のような専用メソッドの代わりに、
    // Method を指定して同じ内容のリクエストを送信できる
    let requests = [
        (
            "GET",
            Method::GET,
            "https://httpbin.org/anything/users".to_string(),
        ),
        (
            "POST",
            Method::POST,
            "https://httpbin.org/anything/users".to_string(),
        ),
        (
            "PUT",
            Method::PUT,
            format!("https://httpbin.org/anything/users/{user_id}"),
        ),
        (
            "DELETE",
            Method::DELETE,
            format!("https://httpbin.org/anything/users/{user_id}"),
        ),
    ];

    for (label, method, url) in requests {
        // RequestBuilder を作成する
        let request_builder = client.request(method, url);

        // レスポンスを取得する
        let response = request_builder.send().await?;

        // ステータスコードを取得する
        let status = response.status();
        println!("[{label}] {status}");
    }

    Ok(())
}
【実行結果】
[GET] 200 OK
[POST] 200 OK
[PUT] 200 OK
[DELETE] 200 OK

request では、例えば、GET であれば、client.request(Method::GET, url) のように reqwest::Method 型を指定することで HTTP メソッドを切り替えます。client.get() などの個別メソッドは、内部的にはこの client.request() を呼び出すラッパーになっています。

呼び出されるメソッドが明確な場合は、各専用メソッドを用いる方が可読性の観点で優れています。一方で、ライブラリなどで動的に呼び出すメソッドを変更したい場合には、request を使って実装するのがおすすめです。

同期通信 reqwest::blocking

この記事の冒頭で説明した通り、reqwest は、非同期モードがデフォルトになっています。同期的に使用したい場合には、reqwest::blocking を使用します。 blocking はデフォルトでは無効なので「cargo add reqwest --features json,query,blocking」のように blocking feature を有効にするようにしてください。

use std::error::Error;
use std::time::Duration;

// blocking::Client を使うと、async/await を使わずに同期的に通信できる
// tokio::main は不要で、通常の fn main で実行できる
fn main() -> Result<(), Box<dyn Error>> {
    // httpbin.org から取得する
    let url = "https://httpbin.org/get";
    // Docker を使用する場合は、以下のように URL を変更してください
    // let url = "http://localhost:8080/get";

    // ClientBuilder でタイムアウトなどを設定する
    let client_builder = reqwest::blocking::Client::builder().timeout(Duration::from_secs(10));

    // ClientBuilder から Client を作成する
    let client = client_builder.build()?;

    // 同じ Client で複数回リクエストを送る
    for count in 1..=3 {
        // RequestBuilder を作成する
        let request_builder = client.get(url);

        // レスポンスを取得する(await が不要)
        let response = request_builder.send()?;

        // ステータスコードを取得する
        let status = response.status();
        println!("{count}回目 ステータスコード: {status}");
    }

    Ok(())
}
【実行結果】
1回目 ステータスコード: 200 OK
2回目 ステータスコード: 200 OK
3回目 ステータスコード: 200 OK

非同期との違いは、まず、main 関数が同期関数となっている点です。#[tokio::main] の付いた関数の中で reqwest::blocking を使うと実行時に panic となるので、非同期との併用は不可である点は注意してください。

また、使い方では、ClientBuilder 生成で reqwest::blocking::Client::builder() を使用する点が異なります。それ以外の使い方はほとんど同様ですが、同期実行であるため、send の際に await は不要となります。

まとめ

Rust で HTTP 通信を行うためのクレートである reqwest の基本的な使い方について、解説しました。

GETPOSTPUTDELETE といった基本的な HTTP メソッドの使い方から、クエリパラメータやヘッダーの指定、JSON のシリアライズ・デシリアライズ、エラー処理の考え方まで、実務でよく使う内容を中心に紹介しました。

また、汎用メソッドの request や、同期的に扱える reqwest::blocking についても紹介しました。基本の使い方をしっかり押さえておくことで、外部 API との連携など、様々な場面で活用できるようになります。ぜひ、基本的な使い方を理解してください。

reqwest の公式ドキュメントは以下を参考にしてください。

ソースコード

上記で紹介しているソースコードについては GitHub にて公開しています。参考にしていただければと思います。

あわせて読みたい
Rust プログラミング入門
Rust プログラミング入門

ABOUT ME
ホッシー
ホッシー
システムエンジニア
はじめまして。当サイトをご覧いただきありがとうございます。
私は製造業のメーカーで、DX推進や業務システムの設計・開発・導入を担当しているシステムエンジニアです。これまでに転職も経験しており、以前は電機メーカーでシステム開発に携わっていました。

これまでの業務を通じてさまざまなプログラミング言語や技術に触れてきましたが、その中でもRustの設計思想に惹かれ、この言語についてもっと深く学びたい、そしてその魅力を発信していきたいと思い、このサイトを立ち上げました。

自身の学びを整理しつつ、同じようにRustに興味を持つ方のお役に立てるような情報を発信していければと思っています。どうぞよろしくお願いいたします。

※キャラクターデザイン:ゼイルン様
記事URLをコピーしました