yotiky Tech Blog

とあるエンジニアの備忘録

Unity (HoloLens) - Httpクライアント の基本的な使い方

Unity (HoloLens) で通信するために使用できる Httpクライアントのサンプルコード集です。
GETとPOSTそれぞれで、コルーチン、UniRx、async/await(TaskとUniTask)などを織り交ぜながら例を書いています。

目次

TL;DR

  • 最新の環境でアセットも自由に使えるのであれば、UnityWebRequest + UniTask を使うのが良いです
  • 環境の制約などある場合はそれぞれの実装例を参考にしてください

概要

対象クラス

対象とするクラスは次の通りです。

クラス 提供元 名前空間 環境
WWW Unity UnityEngine Unity 5.4 より前から
UnityWebRequest Unity UnityEngine.Networking Unity 5.4 以降
HttpWebRequest .NET System.Net .NET Framwork 初期から
HttpClient .NET System.Net.Http .NET Framework 4.5 以降
HttpClient .NET Windows.Web.Http UWP Windows 10.0.10240.0 以降

開発環境

  • Unity : 2019.3.15f.1
  • UniRx : 6.2.2
  • UniTask : 2.0.19
  • MRTK : 2.4.0
  • Visual Studio : 2019

コード全体のプロジェクトはこちらのリポジトリにあります。

github.com

サンプル

WWW

GET

コルーチン

コルーチンを使った実装例です。

    void Start()
    {
        StartCoroutine("GetRequestCoroutine");
    }

    private IEnumerator GetRequestCoroutine()
    {
        var www = new WWW(url);
        yield return www;

        if (www.error != null)
        {
            Debug.Log("failure.");
            Debug.Log(www.error);
        }
        else
        {
            Debug.Log(www.text);
        }
    }

ヘッダをつけたい場合は次のようにします。

    var header = new Dictionary<string, string>() { { "foo", "hoge" } };
    var www = new WWW(url, null, header);

Observable from コルーチン

UniRx を使ってコルーチンを Observable に変換する実装例です。

    void Start()
    {
        Observable.FromCoroutine<string>(observer => GetRequestCoroutine(observer))
            .Subscribe(x => Debug.Log(x))
            .AddTo(this);
    }

    private IEnumerator GetRequestCoroutine(IObserver<string> observer)
    {
        var www = new WWW(url);
        yield return www;

        if (www.error != null)
        {
            Debug.Log("failure.");
        }
        else
        {
            observer.OnNext(www.text);
            observer.OnCompleted();
        }
    }

ObservableWWW

UniRx の ObservableWWW を使った実装例です。

    ObservableWWW.Get(url)
        .Subscribe(
            x => Debug.Log(x),
            ex => Debug.Log(ex.Message),
            () => Debug.Log("completed."))
        .AddTo(this);

進捗を通知したい場合は、 ScheduledNotifier を作って、Get の引数 progress に渡します。

    var progress = new ScheduledNotifier<float>();
    progress.Subscribe(prog => Debug.Log(prog))
        .AddTo(this);
    
    ObservableWWW.Get(url, progress: progress)
        ...

UniTask

UniTask を使った実装例です。UniTask を使うと、yield returnawaitに置き換える(await する)ことができます。

    void Start()
    {
        await GetRequestAsync();
    }
    private async UniTask GetRequestAsync()
    {
        var www = new WWW(url);
        await www;

        if (www.error != null)
        {
            Debug.Log("failure.");
        }
        else
        {
            Debug.Log(www.text);
        }
    }

POST

WWW

POST する場合の実装例です。 WWWForm を使います。それ以外の処理の流れは GET と同じになります。

    var form = new WWWForm();
    form.AddField(key, zipcode);

    var www = new WWW(url, form);

Formのデータと画像(バイナリ)を送信する場合は、WWWFormに追加します。

    var postData = new byte[] { 1, 2 };
    form.AddBinaryData("image", postData, "sample.png", "image/png");

ヘッダをつけて、画像(バイナリ)だけを送信する場合、WWW のコンストラクタに渡します。

    var header = new Dictionary<string, string>() 
    {
        { "foo", "hoge" },
        { "Content-Type", "application/octet-stream" },
    };
    var postData = new byte[] { 1, 2 };
    var www = new WWW(url, postData, header);

ObservableWWW

UniRX の ObservableWWW の実装例です。

    var form = new WWWForm();
    form.AddField(key, zipcode);

    ObservableWWW.Post(url, form)
        .Subscribe(x => Debug.Log(x))
        .AddTo(this);

UnityWebRequest

GET

コルーチン

コルーチンを使った実装例です。

    void Start()
    {
        StartCoroutine("GetRequestCoroutine");
    }

    private IEnumerator GetRequestCoroutine()
    {
        var request = UnityWebRequest.Get(url);
        yield return request.SendWebRequest();

        if (request.isHttpError || request.isNetworkError)
        {
            Debug.Log("failure.");
            Debug.Log(request.error);
        }
        else
        {
            Debug.Log(request.downloadHandler.text);
        }
    }

ヘッダを付ける場合は、次のようになります。

    var request = UnityWebRequest.Get(url);
    request.SetRequestHeader("foo", "hoge");
    request.SetRequestHeader("bar", "fuga");

Observable from コルーチン

UniRx を使ってコルーチンを Observable に変換する実装例です。

    void Start()
    {
        Observable.FromCoroutine<string>(observer => GetRequestCoroutine(observer))
            .Subscribe(x => Debug.Log(x))
            .AddTo(this);
    }

    private IEnumerator GetRequestCoroutine(IObserver<string> observer)
    {
        var request = UnityWebRequest.Get(url);
        yield return request.SendWebRequest();
        
        if (request.isHttpError || request.isNetworkError)
        {
            Debug.Log("failure.");
        }
        else
        {
            observer.OnNext(request.downloadHandler.text);
            observer.OnCompleted();
        }
    }

UniTask

UniTask を使った実装例です。UniTask を導入することで await できるようになります。

    void Start()
    {
        await GetRequestAsync();
    }

    private async UniTask GetRequestAsync()
    {
        var request = UnityWebRequest.Get(url);
        await request.SendWebRequest();

        if (request.isHttpError || request.isNetworkError)
        {
            Debug.Log("failure.");
        }
        else
        {
            Debug.Log(request.downloadHandler.text);
        }
    }

UniTask を Observable に変換したい場合は、Hot/Coldの注意が必要です。

    // 呼び出しと同時に実行される(Hot)
    GetRequestAsync().ToObservable();
    
    // Subscribeするまで実行されない(Cold)
    Observable.Defer(() => GetRequestAsync().ToObservable());

POST

POST する場合の実装例です。 WWWForm を使います。

   var form = new WWWForm();
   form.AddField(key, zipcode);

   var request = UnityWebRequest.Post(url, form);

Form のデータと画像(バイナリ)を送信する場合は、WWWForm に追加します。

    var postData = new byte[] { 1, 2 };
    form.AddBinaryData("image", postData, "sample.png", "image/png");

ヘッダをつけて、画像(バイナリ)だけを送信する場合、UnityWebRequestのヘッダとUploadHandlerを設定します。

    var request = UnityWebRequest.Post(url, form);
    request.SetRequestHeader("foo", "hoge");
    request.SetRequestHeader("Content-Type", "application/octet-stream");

    var postData = new byte[] { 1, 2 };
    request.uploadHandler = new UploadHandlerRaw(postData);
    request.uploadHandler.contentType = "application/octet-stream";
    request.downloadHandler = new DownloadHandlerBuffer();

HttpWebRequest

[Api Compability Level] が [.NET 4.x] であれば直接使えます。

GET

Callbak

コールバックを使った非同期の実装例です。

        try
        {
            var request = WebRequest.Create(url);

            request.BeginGetResponse(x =>
                {
                    var response = (HttpWebResponse)request.EndGetResponse(x);
                    if (response.StatusCode != HttpStatusCode.OK)
                    {
                        Debug.Log("failure.");
                    }
                    else
                    {
                        using (var reader = new StreamReader(response.GetResponseStream()))
                        {
                            var text = reader.ReadToEnd();
                            Debug.Log(text);
                        }
                    }
                },
                null);
        }
        catch (WebException e)
        {
            Debug.Log(e.Message);
        }
        catch (Exception e)
        {
            Debug.Log(e.Message);
        }

ヘッダをつけたい場合は次のようにします。

    request.Headers.Add("foo", "hoge");

Observable

UniRx で Observable に実装する例です。

    Observable.Start(() =>
        {
            var request = WebRequest.Create(url);
            var response = request.GetResponse();

            using (var reader = new StreamReader(response.GetResponseStream()))
            {
                return reader.ReadToEnd();
            }
        })
        .Subscribe(x => Debug.Log(x))
        .AddTo(this);

GetResponse には代替となる拡張メソッドが用意されてたりもします。

    WebRequest.Create(url)
        .GetResponseAsObservable()
        .Select(res =>
        {
            using (var reader = new StreamReader(res.GetResponseStream()))
            {
                return reader.ReadToEnd();
            }
        })
        .Subscribe(
            x => Debug.Log(x),
            error => Debug.Log(error.Message),
            () => Debug.Log("completed."))
        .AddTo(this);
}

UniTask (Task)

async/await を使った実装例です。UniTask は Task に書き換えても動きます。

    void Start()
    {
        await GetRequestAsync();
    }

    private async UniTask GetRequestAsync()
    {
        var request = WebRequest.Create(url);
        var response = await request.GetResponseAsync();
        using (var reader = new StreamReader(response.GetResponseStream()))
        {
            var text = reader.ReadToEnd();
            Debug.Log(text);
        }
    }

POST

POST する場合の実装例です。

    var request = WebRequest.Create(url);
    request.Method = "POST";

    var param = $"{key}={zipcode}";
    var data = Encoding.ASCII.GetBytes(param);

    request.ContentType = "application/x-www-form-urlencoded";
    request.ContentLength = data.Length;
    using (var stream = request.GetRequestStream())
    {
        stream.Write(data, 0, data.Length);
    }

画像(バイナリ)を送信する場合の実装例です。

    var postData = new byte[] { 1, 2 };
    request.ContentType = "image/png";
    request.ContentLength = postData.Length;
    using(var stream = request.GetRequestStream())
    {
        stream.Write(postData, 0, postData.Length);
    }

ヘッダを付けて、画像(バイナリ)を送信する場合の実装例です。

    request.Headers.Add("foo", "hoge");
    request.Headers.Add("Content-Type", "application/octet-stream");
    var postData = new byte[] { 1, 2 };
    request.ContentType = "image/png";
    request.ContentLength = postData.Length;
    using (var stream = request.GetRequestStream())
    {
        stream.Write(postData, 0, postData.Length);
    }

HttpClient (System.Net.Http)

GET

Task を使った実装例です。 UniTask に書き換えても動きます。

    private async Task GetRequestAsync()
    {
        var content = new FormUrlEncodedContent(new Dictionary<string, string>
        {
            { key, zipcode },
        });
        var encodedQuery = await content.ReadAsStringAsync();

        var response = await httpClient.GetAsync($"{url}?{encodedQuery}");

        if (!response.IsSuccessStatusCode)
        {
            Debug.Log("failure.");
            Debug.Log(response.ReasonPhrase);
        }
        else
        {
            var text = await response.Content.ReadAsStringAsync();
            Debug.Log(text);
        }
    }

ヘッダを付ける場合の実装例です。

    var request = new HttpRequestMessage(HttpMethod.Get, url);
    request.Headers.Add("foo", "hoge");
    var response = await httpClient.SendAsync(request);

次のようにもできますが、HttpClient のデフォルトとなっているので注意が必要です。

    httpClient.DefaultRequestHeaders.Add("foo", "hoge");

async/await が使えない場合には、Task.Run が使えます。

    Task.Run(async () =>
    {
        await Task.Delay(3000);
    });

POST

POST する場合の実装例です。 GetAsync の代わりに PostAsync を使います。

    var content = new FormUrlEncodedContent(new Dictionary<string, string>
    {
        { key, zipcode.ToString() },
    });

    var response = await httpClient.PostAsync(url, content);

画像(バイナリ)を送信する場合の実装例です。

    var postData = new byte[] { 1, 2 };
    var byteContent = new ByteArrayContent(postData);
    byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
    var res = await httpClient.PostAsync(url, byteContent);

こちらは HttpRequestMessage を使った例です。呼び出す時は SendAsync に代わります。

    var request = new HttpRequestMessage(HttpMethod.Post, url);
    var postData = new byte[] { 1, 2 };
    var byteContent = new ByteArrayContent(postData);
    request.Content = byteContent;
    request.Content.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
    var res = await httpClient.SendAsync(request);

MultipartFormDataContent を使った実装例です。

    var request = new HttpRequestMessage(HttpMethod.Post, url);
    request.Headers.Add("foo", "hoge");
    var postData = new byte[] { 1, 2 };
    var byteContent = new ByteArrayContent(postData);
    byteContent.Headers.Add("Content-Type", "image/jpeg");

    var data = new MultipartFormDataContent();
    data.Add(new StringContent(zipcode.ToString()), key);
    data.Add(byteContent, "file", "test.jpg");
    request.Content = data;
    var res = await httpClient.SendAsync(request);

サーバーサイドでバイナリを取り出す時には方法が異なるので注意が必要です。
以下は Azure Functions での例です。

ByteArrayContent を直接使った場合は、Body から取り出します。

    using(var ms = new MemoryStream())
    {        
        req.Body.CopyTo(ms);
        ms.Position = 0;
    }

MultipartFormDataContent を使った場合は、File から取り出します。

    using(var ms = new MemoryStream())
    {
        req.Form.Files[0].CopyTo(ms);
        ms.Position = 0;
    }

HttpClient (Windows.Web.Http)

UWP の WinRT API なので、すべてのコードは #if WINDOWS_UWP で囲む必要があります。

GET

Task を使った実装例です。 UniTask に書き換えても動きます。

using Windows.Web.Http;
    private static readonly HttpClient httpClient = new HttpClient();

    public async UniTask GetRequestAsync()
    {
        var builder = new UriBuilder(url);
        builder.Query = query;

        var response = await httpClient.GetAsync(builder.Uri);

        if (!response.IsSuccessStatusCode)
        {
            Debug.Log("failure.");
            Debug.Log(response.ReasonPhrase);
        }
        else
        {
            var text = await response.Content.ReadAsStringAsync();
            Debug.Log(text);
        }
    }

ヘッダを付ける場合の実装例です。HttpRequestMessage を使って設定します。

    var request = new HttpRequestMessage(HttpMethod.Get, builder.Uri);
    request.Headers.Add("foo", "hoge");
    var response = await httpClient.SendRequestAsync(request);

次のようにもできますが、HttpClient のデフォルトとなっているので注意が必要です。

    httpClient.DefaultRequestHeaders.Add("foo", "hoge");

POST

POST する場合の実装例です。 GetAsync の代わりに PostAsync を使います。

    var content = new HttpFormUrlEncodedContent(new Dictionary<string, string>
        {
            { key, zipcode.ToString() },
        });

    var uri = new Uri(url);
    var response = await httpClient.PostAsync(uri, content);
    var text = await response.Content.ReadAsStringAsync();
    Debug.Log(text);

ヘッダを付けて画像(バイナリ)を送信する場合の実装例です。MultipartFormDataContent を使っています。

    var request = new HttpRequestMessage(HttpMethod.Post, uri);
    request.Headers.Add("foo", "hoge");
    var file = await KnownFolders.CameraRoll.GetFileAsync("test.jpg");
    var buffer = await FileIO.ReadBufferAsync(file);
    var byteContent = new HttpBufferContent(buffer);
    byteContent.Headers.Add("Content-Type", "image/jpeg");

    var data = new HttpMultipartFormDataContent();
    data.Add(new HttpStringContent(zipcode.ToString()), key);
    data.Add(byteContent, "file", "test.jpg");
    request.Content = data;

    var res = await httpClient.SendRequestAsync(request);

参考

後から見直したくなりそうなものや、一度目を通しておいたほうが良さそうなものなどを残しておきます。

Unity

.NET 一般

サービス

  • PTS - V2
    • POSTの検証用サーバー「Post Test Server V2」
    • IDを入力するか、ランダムのボタンを押すと専用APIのページが表示される
    • リクエストを投げて画面を更新すると、POSTの内容がダンプされるので便利
    • POSTだけでなくGETでも使える
      f:id:yotiky:20200620010650p:plain:w400
      f:id:yotiky:20200620010858p:plain:w300