63357


reqwestを使って、とあるJSON
APIを呼び出そうとしています。レスポンスを構造体に格納して`Result`に含んで返し、外部関数から呼び出せるようにしたいのですが、コンパイルが通らず、しばらく格闘したのですが解決方法が見つからないため、ご教授いただきたいです。

**ソースコード**

[code]

    const STEAM_API_KEY: &str = "****";
    const STEAM_ID: &str = "****";
    
    use serde::{Deserialize, Serialize};
    
    #[derive(Serialize, Deserialize, Debug)]
    struct Stats {
        detail: Detail,
    }
    
    #[derive(Serialize, Deserialize, Debug)]
    struct Detail {
        name: String,
        value: i64,
    }
    
    #[derive(Serialize, Deserialize, Debug)]
    struct PlayerStats {
        steamid: String,
        gamename: String,
        stats: Stats,
    }
    
    pub fn stats() -> Result<PlayerStats, reqwest::Error> {
        let url = format!(
            "http://api.steampowered.com/ISteamUserStats/GetUserStatsForGame/v0002/?appid=730&key={}&steamid={}",
            STEAM_API_KEY,
            STEAM_ID
        );
    
        let resp: PlayerStats = reqwest::get(&url)?.json()?;
    
        Ok(resp)
    }
    
    
[/code]

**エラーメッセージ**

[code]

    error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try`
      --> src/lib.rs:45:29
       |
    45 |     let resp: PlayerStats = reqwest::get(&url)?.json()?;
       |                             ^^^^^^^^^^^^^^^^^^^ the `?` operator cannot be applied to type `impl std::future::Future`
       |
       = help: the trait `std::ops::Try` is not implemented for `impl std::future::Future`
       = note: required by `std::ops::Try::into_result`
    
[/code]


CC BY-SA 4.0
2020-02-25T01:16:39.533
63331
2020-03-01T02:22:35.647
2020-02-25T01:31:47.973
3060
34283
question
rust
Rustのreqwestを使ったJSON APIの扱いについて


他の方からのコメントにもありますが、reqwestは0.10から非同期IO（async/await）に対応し、APIが変わりました。従来の方法（同期式のブロッキングIO）を行うときは、モジュール名の書き換えが必要になります。

ブロッキングIOの使い方ですが、まず、`Cargo.toml`の`dependency`を以下のように書き換えて、reqwestクレートの`blocking`フィーチャをオンにします。

[code]

    // Cargo.toml
    [dependencies]
    serde = { version = "1.0", features = ["derive"] }
    reqwest = { version = "0.10", features = ["blocking", "json"] }
    
[/code]

その上で、`reqwest::get()`の行を以下のように変更します。

[code]

    // Request 0.10からはブロッキングIOを行うときはblockingモジュールを使う
    let resp: PlayerStats = reqwest::blocking::get(&url)?.json()?;
    
[/code]

`blocking`モジュールの使い方についてはこちらを参照してください。  
<https://docs.rs/reqwest/0.10.2/reqwest/blocking/index.html>

なお、複数のリクエストを同時に処理したいときは、ブロッキングIOよりも非同期IOの方が効率よく処理できます。その代わり非同期IOではコードの記述量は増えてしまいます。用途に応じて使い分けてください。

**追記**

> 加えてお聞きしたいのですが、ここで得たレスポンスの中身をtestで標準出力へと受け渡したいのですが、いい方法はありますでしょうか。

デバッグ目的なら`dbg!`マクロを使うのが便利です。  
<https://doc.rust-lang.org/std/macro.dbg.html>

[code]

    let resp: PlayerStats = dbg!(reqwest::blocking::get(&url)?).json()?;
    
[/code]

ただし、`reqwest::blocking::Response`型ですと上のように書いたときに表示されるのは、URL、レスポンスのHTTPステータス、レスポンスヘッダだけになります。レスポンスボディは表示されません。これは`Response`型が、`json()`や`text()`メソッドなどを呼ばない限り、ネットワークからレスポンスボディを受信しない仕様になっているからだと思います。また、一つの`Response`値に対して、`json()`または`text()`メソッドを呼べるのは1回限りとなっています。

レスポンスボディも表示したい場合は、以下のように同じURLに対して`get()`を2回実行するのが簡単だと思います。

[code]

        // dbg!でURL、レスポンスのHTTPステータス、レスポンスヘッダを表示する
        let resp = dbg!(reqwest::blocking::get(&url)?);
        // textでレスポンスボディを受信し、dbg!で表示する
        dbg!(resp.text()?);
    
        // jsonをパースするために、getをもう一度実行する
        let stats: PlayerStats = reqwest::blocking::get(&url)?.json()?;
        Ok(stats)
    
[/code]

もう一つの方法は、`Response`型の`json()`メソッドを使うのはやめて、serde_jsonの`serde_json::from_str()`関連関数を使うことです。この方法ですと`get()`は1回実行するだけで済みます。

[code]

    // Cargo.tomlのdependenciesセクションにserde_jsonを追加する
    // serde_json = "1.0"
    
    // stats()の戻り値型のエラー型を変更して、reqwestのエラーとserde_jsonのエラーの
    // どちらでも返せるようにする。
    pub fn stats() -> Result<PlayerStats, Box<dyn std::error::Error>> {
        let url = ...（略）;
    
        // dbg!でURL、レスポンスのHTTPステータス、レスポンスヘッダを表示する
        let resp = dbg!(reqwest::blocking::get(&url)?);
        // textでレスポンスボディを受信し、dbg!で表示する
        let body = dbg!(resp.text()?);
    
        // serde_jsonのfrom_strでJSONをパースする
        let stats: PlayerStats = serde_json::from_str(&body)?;
        Ok(stats)
    }
    
[/code]


CC BY-SA 4.0
2020-02-26T01:56:39.367
63357
2020-03-01T02:22:35.647
2020-03-01T02:22:35.647
14101
14101
63331
answer