はじめに
**「UnityでHTTP通信してみる」**というテーマでTechTrainアドベントカレンダーを書いていきたいと思います!
こういった記事はすでにいくつも存在していますが、コードが掲載されていても「サーバに実際にリクエストを送ってレスポンスを受け取る」という処理を自分のローカルでも試してみるためにはサーバが要りますよね。
やっぱりプログラミングは手を動かしてみることも大事だと思うので、簡単なAPIサーバをGoで書いたプロジェクトを作ったので、もしよければ下記のリンクからcloneして、ローカルで動かしながら実際にHTTP通信を実装していただければより深い学びにつながると思います!
sample-serverのgithubリンク
HTTP/GET
まずは、HTTP/GETから実装していきます。GETリクエストの場合は、クライアントから特にデータを含めたりはせず、特定のエンドポイントに対してリクエストを送信して、返ってきた結果を処理する形が主だと思います。なので、今回も特にBodyにデータを含めたりはせず、JSON形式でレスポンスを受け取るという実装をしていきます。
private IEnumerator GET()
{
using (var req = UnityWebRequest.Get("http://localhost:80/user"))
{
yield return req.SendWebRequest();
if (req.isNetworkError)
{
resultText.text = req.error;
}
else if (req.isHttpError)
{
resultText.text = req.error;
}
else
{
resultText.text = req.downloadHandler.text;
}
}
}
コードの解説をしていきます。
でHTTPリクエストを作成します。
UnityWebRequest.Get(string uri)
は、UnityWebRequestクラスで定義された静的メソッドで、URIで指定されたエンドポイントからデータを取得します。このメソッドを呼び出せば、標準のDownloadHandlerBufferを返り値のUnityWebRequestに設定するため、明示的に設定する必要はありません。
また、このメソッドは**UploadHandlerをアタッチしません。**これは、HTTP/GETの場合カスタムのヘッダーやデータを含めることがないからです。
UnityWebRequestを作成したら、次にリクエストを実際に送信します。送信している部分が下記のコードです。
yield return req.SendWebRequest();
非同期処理になるので、コルーチンを使って、処理が完了するまで待機するようになっています。
次にエラー処理ですが、エラーの種類は大きく下記の二種類に分けられます。
- isNetworkError
- isHttpError
前者のisNetworkErrorはUntyWebRequestがシステムエラーを起こした際にtrueになるもので、
後者のisHttpErrorはレスポンスのステータスコードがエラーコードだった場合にtrueになります。
なお、isErrorという変数もありますが、こちらは非推奨となっているため使用を避ける方がいいと思います。
HTTP/GETリクエストを送信するフローとして、
- UnityWebRequestを作成
- SendWebRequest()を使ってリクエストを送信
- isNetworkErrorとisHttpErrorでエラーハンドリング
の3ステップで実装できます。sample-serverの/userに対してGETリクエストを送信するとダミーのUserデータが返されるのでぜひ実行して試してみてください!
HTTP/POST
次はbodyにデータを含めてHTTP/POSTリクエストを送信するところまで実装してみます。今回はJSON形式でデータを送信し、レスポンスを受け取る処理を解説します。
まず、HTTP通信の際のbodyにデータを含めるためにデータをシリアライズしてbyte[]に変換する必要があります。最初にそこの実装を解説していきます。
送信するデータや、レスポンスとして返ってくるデータの形式をモデルクラスとして定義する必要があります。この辺は、自分で書いてもいいのですが、JSONデータから自動的にモデルクラスを生成してくれるツールもあるので、面倒な場合や工数削減にこう言ったツールを使うのも良いかと思います。あと、モデルクラスの静的関数にSerialize/Deserialize用のメソッドを定義しておくとコードが書きやすいと思います。
using System;
using UnityEngine;
[Serializable]
public class User
{
public string id;
public string name;
public int age;
public static User Deserialize(string json)
{
User user = JsonUtility.FromJson<User>(json);
return user;
}
public static string Serialize(User model)
{
string json = JsonUtility.ToJson(model);
return json;
}
}
まずは、SerializableAttributeについて。オブジェクトをシリアル化する際にこの属性を付与していないと例外が発生します。シリアル化とは、オブジェクトをバイナリ形式に変換することだと思ってもらえば大丈夫です。モデルクラスにはまずこの属性をつける必要があります。
次に、モデルクラスを文字列のJSONに変換するにはUnityAPIに存在するJsonUtilityを使用します。
ただ、このJsonUtilityはジェネリックに対応していなかったり、エラー時にnullを返してきたりといろいろ扱いづらい部分もあります。もちろんサクッと使うには便利だったりするのですが、必要であればJSON.NETなどを利用することも考えてみるといいと思います。
ただ、以前JSON.NETを使ってiOSアプリを開発している際に、ジェネリクスのパースの際にリフレクションを使っているため、IL2CPPを使ってiOS向けなどにビルドする場合にビルドエラーが発生しました。その時の解決策としてジェネリクスを使わないという方法で解決したのですが、完成してからビルドしたときに使えないとならないように事前に確認しておくと良いかと思います。
文字列のJSONにパースできたら、それをbyte[]にシリアライズする必要があります。これはC#のSystem.Text空間に定義されているメソッドを利用してエンコードしていきます。
Encoding.UTF8.GetBytes(json);
ここまでで、モデルクラスをbyte[]にシリアライズすることができます。送信したいデータをシリアライズできたら、UnityWebRequestを作成し、メソッドにPOSTを指定した上でヘッダーを登録するなどの処理を行なってリクエストを送信します。この辺りの実装は下記の通りになります。
using (var req = new UnityWebRequest("http://localhost:80/user", UnityWebRequest.kHttpVerbPOST))
{
req.uploadHandler = (UploadHandler)new UploadHandlerRaw(body);
req.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
req.SetRequestHeader("Content-Type", "application/json");
yield return req.SendWebRequest();
}
レスポンスのJSONをデシリアライズする
ここまでどうやってHTTPリクエストを送信するかについて解説してきましたが、最後にレスポンスのJSONをどうやってモデルクラスにデシリアライズするかについて解説していきます。
まず、レスポンスボディのデータを取得する方法ですが、downloadHandlerのtextプロパティにアクセスすることで、UTF8エンコーディングの文字列が取得できます。
この文字列をUnityのJsonUtilityで定義されたFromJsonメソッドを使用してデシリアライズしていきます。
var response = User.Deserialize(req.downloadHandler.text);
Debug.Log($"ID:{response.id}\nName:{response.name}\nAge:{response.age}");
おまけ
HTTPメソッドの文字列は全てUnityWebRequestのStatic変数として定義されています。ハードコーディングしてタイポしてるってことがないようにこちらを使うことをお勧めします。
UnityWebRequestのスクリプトリファレンス
レスポンスのステータスコードは、
UnityWebRequestのresponseCode変数にアクセスすることで確認できます。
ステータスコードに合わせて処理を変更したい場合などは、こちらを使用してください。
Debug.Log($"StatusCode: {req.responseCode}");
まとめ
今回は「UnityでHTTP通信する」方法についてまとめました!何よりもさらっと読んだ方は実際にサーバのコードもこちら用意しているので自分で実装してみて試してみてください!