UnityWebRequest で簡単 HTTP(POST)通信


お疲れ様です。
システム開発室、松山です。

今回は UnitWebRequest を使った HTTP(POST) 通信について書いていきます。
今更感満載ですが、私が www での通信しか実装してなかったので改めて ^^;

作るもの

・UnitWebRequst で HTTP(POST)通信
・リクエスト、レスポンスは JSON 形式
・複数の通信 API を作成する想定でシンプルに
※ Unity 2019.3.5f1 で作成

JSON

まず JSON ですが、Unity の JsonUtility を使って実装します。
JsonUtility は Unity が提供している Json パーサで、
Serializable 指定されたクラスや構造体を用意にシリアライズできます。
Dictionary 型はサポート外など制約もありますが、パフォーマンス面で他の Json パーサより優れているようです。
Json 形式にシリアライズ

UnityWebRequest

今回メインの通信処理です。
UnityWebRequest で HTTPリクエストとレスポンスを扱うことができます。
UnityWebRequest

処理自体はかなりシンプルに書けました。
UnityWebRequest を POST形式で生成し、リクエストパラメータをjson形式で設定します。

    // HTTP(POST)の情報を設定
    var req = new UnityWebRequest(url, "POST");
    req.uploadHandler = (UploadHandler)new UploadHandlerRaw(postData);
    req.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
    req.SetRequestHeader("Content-Type", "application/json");

1点注意なのは、パラメータは string のままでは渡せないため、byte配列に変換することでしょうか。

    // リクエストオブジェクトを JSON に変換(byte配列)
    string reqJson = JsonUtility.ToJson(request);
    byte[] postData = System.Text.Encoding.UTF8.GetBytes(reqJson);

準備できたらリクエストを投げます。

    // API 通信(完了待ち)
    yield return req.SendWebRequest();

通信結果は isNetworkError と isHttpError がともに false であることで成功と判定しました。
レスポンスはdownloadHandler に Json形式(text)で格納されます。

    // 通信結果
    if (req.isNetworkError ||
        req.isHttpError)  // 失敗
    {
        Debug.Log("Network error:" + req.error);
    }
    else                  // 成功
    {
        Debug.Log("Succeeded:" + req.downloadHandler.text);
    }

API 制御

上述の通信処理を基底クラスとして、各API のリクエストとレスポンスを定義した APIクラスを作成するイメージ。
具体的にはこういった形でしょうか。
リクエストとレスポンス用の構造体を Serializable 属性で作成し、パラメータを定義。
通信時の URL は共通部分を基底クラス側で定義するので、末尾の API 名を定義。

using System;
using System.Collections.Generic;

namespace Api
{
    /// <summary>
    /// API サンプルB
    /// </summary>
    public class SampleB : Web.ApiBase
    {
        public const string Name = "Test/SampleB";

        /// <summary>
        /// リクエストパラメータ
        /// </summary>
        [Serializable]
        public struct Request
        {
            public string userId;
            public List<int> values;
        }
        public Request request;

        /// <summary>
        /// レスポンスパラメータ
        /// </summary>
        [Serializable]
        public struct Response
        {
            public int count;
            public List<int> values;
        }
        public Response response;
    }
}

使い方

まず、上述の API クラスを生成。

    private Api.SampleB         apiB;
    private Web.ApiBase.Result  result;

    private void Start()
    {
        // 通信用クラス生成
        apiB = new Api.SampleB();

        // API 通信
        sendApiB();
    }

API 名を指定したら、リクエスト構造体に渡す値を設定。
メソッド Send() 内で JSON 形式にシリアライズされ、UnityWebRequest で HTTP(POST) 通信されます。
通信完了をコールバックで受け取り、レスポンス構造体へデシリアライズします。
本来はレスポンスの内容に応じて処理を書くと思いますが、サンプルなのでコンソールに出力するだけにしています。

    /// <summary>
    /// API サンプルB 通信
    /// </summary>
    private void sendApiB()
    {
        // エンドポイントの設定
        apiB.EndPoint = Api.SampleB.Name;
        // リクエストパラメータを設定
        apiB.request.userId = "beyondB";
        apiB.request.values = new List<int>() { 1, 10, 100, 1000 };

        // 通信
        apiB.Send<Api.SampleB.Request>(ref apiB.request, result => {
            // リザルト
            if (result.isSucceeded)  // 成功
            {
                // レスポンスを展開
                apiB.response = apiB.Response<Api.SampleB.Response>();

                // 内容確認
                Debug.Log("SampleB Succeed!!");
                Debug.Log("  count : " + apiB.response.count);
                foreach(var v in apiB.response.values)
                {
                    Debug.Log("  val : " + v);
                }
            }
            else                    // 失敗
            {
                Debug.Log("SampleB Failed : " + result.error);
            }
        });
    }

まとめ

といった形で、かなりシンプルに HTTP通信処理を書くことができました。
とりあえず実用にも耐えるのではないかなと思います。
通信処理なのでサーバー側の実装がないと動作しませんが、一応サンプルコードを GitHub に上げておきます。
Unity サンプル

サンプルの基底クラスでは MonoBehaviour を継承していないので、そのままではコルーチンが使えませんでした。
こちらを参考に対応しています。
MonoBehaviorを継承しないクラスでコルーチンを使う
それでは、今回のお話はこれまでになります。


この記事をかいた人

About the author

松山賢勝

2019年8月から横浜オフィスに勤務。
経歴としてはクライアント開発が多いため、クライアント寄りの記事が多いかも。
趣味は自転車(ロード)と競馬。