---
title: "UnityWebRequestの使い方｜GET・POST(JSON)・timeout・エラー処理とawait【Unity 6対応】"
url: "https://www.issoh.co.jp/tech/details/6891/"
published: 2025-05-23
updated: 2026-10-17
categories: ["シミュレーション"]
publisher: "株式会社一創"
---

# UnityWebRequestの使い方｜GET・POST(JSON)・timeout・エラー処理とawait【Unity 6対応】

UnityWebRequestは、UnityでHTTP通信をするための標準クラスです（名前空間は`UnityEngine.Networking`）。この記事では、GETでデータを取る・POSTでJSONを送る・`timeout`を設定する・`result`で失敗を見分ける、という実装に必要な手順を、Unity 6向けのサンプルコードで説明します。2017〜2018年の解説に残る`isNetworkError`や`Post(url, json)`が、現行版で何に変わったかも示します。Unity 6系そのものの変更点は[Unity 6.3 LTS（6000.3）の新機能と変更点｜最新パッチ・サポート期限・6.0からの移行](/tech/details/10417/)にまとめています。

## まとめ

- 流れは「生成→`SendWebRequest()`で送信→`result`で判定→`using`で破棄」の4手順。GETは`UnityWebRequest.Get(url)`。
- JSONのPOSTは、Unity 2022.2以降なら`UnityWebRequest.Post(url, json, "application/json")`。引数2つの`Post(url, json)`は文字列をURLエンコードしてフォーム形式で送るため、JSON APIには届かない。
- `timeout`はint型の秒数で、既定値0は「時間制限なし」。送信後に設定すると例外になるので、`SendWebRequest()`の前に入れる。
- 判定は`request.result`の5値で行う。`isNetworkError`・`isHttpError`はObsolete。再試行は一時的な失敗（接続断・408・429・500・502・503・504など）に絞り、API仕様と冪等性で最終判断する。
- Unity 2023.1以降（Unity 6を含む）は`await request.SendWebRequest()`と書ける。
- 通信できないときは、HTTP接続の禁止（Unity 2022.1以降の既定）とWebGLでのCORSを確認する。資源管理では`Dispose`を忘れない。旧WWWクラスはUnity 6でもObsoleteのまま残っている。

## UnityWebRequestの基本：生成・送信・判定・破棄の4手順

UnityWebRequestを使うには、スクリプトの先頭に`using UnityEngine.Networking;`を書きます。どの通信も、次の4手順で書けます。

1. `UnityWebRequest.Get(url)`などの静的メソッドでリクエストを作る
2. `SendWebRequest()`で送信し、コルーチンなら`yield return`、async関数なら`await`で完了を待つ
3. `request.result`で成否を判定し、本文を`request.downloadHandler.text`で読む
4. `using`で囲んで破棄する（成功・失敗どちらでも`Dispose`が必要）

作成用のメソッドは用途ごとに分かれています。

| 用途          | 作成メソッド                                                   | 受信側                        |
| ----------- | -------------------------------------------------------- | -------------------------- |
| 取得          | `UnityWebRequest.Get(url)`                               | DownloadHandlerBuffer      |
| JSON送信      | `UnityWebRequest.Post(url, json, contentType)`           | DownloadHandlerBuffer      |
| フォーム送信      | `UnityWebRequest.Post(url, WWWForm)`                     | DownloadHandlerBuffer      |
| 更新・削除       | `Put(url, body)` ／ `Delete(url)`                         | Deleteは受信側なし               |
| 画像          | `UnityWebRequestTexture.GetTexture(url)`                 | DownloadHandlerTexture     |
| 音声          | `UnityWebRequestMultimedia.GetAudioClip(url, AudioType)` | DownloadHandlerAudioClip   |
| AssetBundle | `UnityWebRequestAssetBundle.GetAssetBundle(url)`         | DownloadHandlerAssetBundle |

`new UnityWebRequest(url, "PATCH")`のようにコンストラクターで任意のメソッド名を渡すこともできます。この場合は`uploadHandler`と`downloadHandler`を自分で付けます。

## GETでのデータ取得：コルーチンの実装例

MonoBehaviourの`Start`をコルーチン（`IEnumerator`）にすると、そのまま`yield return`で送信完了を待てます。次のコードは、取得に成功したら本文を表示し、HTTPエラーと通信エラーを分けてログに出します。

この記事のサンプルは、UnityCsReference（6000.6.0f1）のAPIシグネチャに対して型が合うことを確認したもので、Unityエディタでの通信動作は確認していません。`https://example.com/...`は仮のURLなので、自分のAPIのURLに置き換えてください。クラス全体を載せていない断片は、MonoBehaviourを継承したクラスの中に置き、`IEnumerator`のメソッドとして`StartCoroutine`で呼び出します。

```
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;

public class GetSample : MonoBehaviour
{
    IEnumerator Start()
    {
        using (UnityWebRequest request = UnityWebRequest.Get("https://example.com/api/score"))
        {
            request.timeout = 10; // 送信前に設定する（秒）
            yield return request.SendWebRequest();

            switch (request.result)
            {
                case UnityWebRequest.Result.Success:
                    Debug.Log(request.downloadHandler.text);
                    break;
                case UnityWebRequest.Result.ProtocolError: // 404や500など
                    Debug.LogWarning($"HTTP {request.responseCode}: {request.downloadHandler.text}");
                    break;
                default: // ConnectionError / DataProcessingError
                    Debug.LogError($"{request.result}: {request.error}");
                    break;
            }
        }
    }
}
```

受け取ったJSONをC#のクラスに変換するには`JsonUtility.FromJson<T>(request.downloadHandler.text)`を使います。変換先のクラスには`[System.Serializable]`を付け、フィールドはpublicにします（次章の`ScoreResponse`を参照）。[JSONシリアライズのマニュアル](https://docs.unity3d.com/6000.3/Documentation/Manual/json-serialization.html)のとおり、JsonUtilityは`Dictionary`に対応せず、配列やプリミティブ型を直接渡すこともできません。APIが配列を返すときは`{"items":[...]}`の形にラップしてから変換するか、Newtonsoft.Jsonなど別のJSONライブラリを使います。

URLのクエリに日本語や記号を入れるときは、`UnityWebRequest.EscapeURL("Fish & Chips")`でエスケープしてから連結します。

## POSTでのデータ送信：JSON・フォーム・2022.1以前の書き方

### JSONの送信：Post(url, json, “application/json”)

Unity 2022.2で、Content-Typeを第3引数に取る[UnityWebRequest.Postのオーバーロード](https://docs.unity3d.com/2022.2/Documentation/ScriptReference/Networking.UnityWebRequest.Post.html)が追加されました。文字列はUTF-8のバイト列として`UploadHandlerRaw`に入り、受信用の`DownloadHandlerBuffer`も自動で付きます。Unity 6でJSONを送るなら、この書き方が最短です。

```
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;

[System.Serializable]
public class ScoreRequest { public string userId; public int score; }

[System.Serializable]
public class ScoreResponse { public int rank; public string message; }

public class PostJsonSample : MonoBehaviour
{
    IEnumerator Start()
    {
        string json = JsonUtility.ToJson(new ScoreRequest { userId = "u001", score = 1200 });

        // Unity 2022.2以降：第3引数でContent-Typeを指定する
        using (UnityWebRequest request = UnityWebRequest.Post("https://example.com/api/score", json, "application/json"))
        {
            request.timeout = 10;
            yield return request.SendWebRequest();

            if (request.result != UnityWebRequest.Result.Success)
            {
                Debug.LogError($"{request.responseCode} {request.error}");
                yield break;
            }
            ScoreResponse res = JsonUtility.FromJson<ScoreResponse>(request.downloadHandler.text);
            Debug.Log($"rank={res.rank}");
        }
    }
}
```

### Post(url, json)ではJSONが届かない理由

引数2つの`Post(string uri, string postData)`は、Unity 2022.2以降Obsoleteです。[UnityCsReferenceのソース（6000.6.0f1）](https://github.com/Unity-Technologies/UnityCsReference/blob/6000.6.0f1/Modules/UnityWebRequest/Public/WebRequestExtensions.cs)を読むと、このメソッドは`postData`を`WWWTranscoder.WwwFormEncode`でURLエンコードし、Content-Typeを`application/x-www-form-urlencoded`にして送ります。そのため`{"score":1200}`は`%7B%22score%22...`のような文字列になり、JSONを期待するサーバーは解釈できません。後から`SetRequestHeader("Content-Type", "application/json")`を足しても、本文はエンコード済みのままです。

コンパイラ警告のメッセージは「Content-Type引数付きのPostかPostWwwFormを使え」で、自動更新（UnityUpgradable）の置換先は`PostWwwForm`になっています。API Updaterに任せると、JSONを送っていた箇所もフォーム送信のままになります。JSON APIを呼んでいる箇所は、手でContent-Type付きの`Post`に書き換えてください。

### Unity 2022.1以前のJSON送信：UploadHandlerRaw

2022.1以前のプロジェクトでは、`new UnityWebRequest(url, "POST")`にハンドラーを自分で付けます。

```
IEnumerator PostJson(string url, string json)
{
    byte[] body = System.Text.Encoding.UTF8.GetBytes(json);
    using (UnityWebRequest request = new UnityWebRequest(url, "POST"))
    {
        request.uploadHandler = new UploadHandlerRaw(body);
        request.downloadHandler = new DownloadHandlerBuffer(); // 付けないとレスポンス本文を読めない
        request.SetRequestHeader("Content-Type", "application/json");
        yield return request.SendWebRequest();
        Debug.Log($"{request.responseCode} {request.error}"); // resultは2020.1以降
    }
}
```

コンストラクターで作ったリクエストには`downloadHandler`が付きません。付け忘れると、送信は成功しても`request.downloadHandler`がnullでレスポンス本文を読めません。

### フォームの送信：WWWFormとPostWwwForm

HTMLフォームと同じ形式で送るときは`WWWForm`を使います。画像などのファイルを添付する場合は`form.AddBinaryData(フィールド名, バイト列, ファイル名, MIMEタイプ)`を使います。MIMEタイプを省略すると、先頭8バイトがPNGの形式ならimage/png、それ以外はapplication/octet-streamとして送られます。

```
WWWForm form = new WWWForm();
form.AddField("name", "player1");
form.AddField("score", 1200);

using (UnityWebRequest request = UnityWebRequest.Post("https://example.com/form", form))
{
    yield return request.SendWebRequest();
    Debug.Log(request.result);
}
```

すでに`name=player1&score=1200`の形の文字列を持っているなら、`UnityWebRequest.PostWwwForm(url, "name=player1&score=1200")`でも送れます。

## リクエストヘッダーの設定とレスポンスヘッダーの取得

リクエストヘッダーは`SetRequestHeader(名前, 値)`で付けます。認証トークンは`Authorization`ヘッダーに入れるのが一般的です。[公式リファレンス](https://docs.unity3d.com/6000.3/Documentation/ScriptReference/Networking.UnityWebRequest.SetRequestHeader.html)のとおり、`SendWebRequest()`の後には呼べません。

```
using (UnityWebRequest request = UnityWebRequest.Get("https://example.com/api/me"))
{
    request.SetRequestHeader("Authorization", "Bearer " + token);
    request.SetRequestHeader("Accept", "application/json");
    yield return request.SendWebRequest();

    Debug.Log(request.GetResponseHeader("Content-Type"));
}
```

自由に設定できないヘッダーもあります。

- `Accept-Encoding`は対応する圧縮形式が自動で入り、別の値は無視されるか失敗の原因になります。`Content-Length`も自動で設定されます。
- `Host`・`Origin`・`Referer`・`Connection`などは、プラットフォームによって上書き・無視されるため、設定しないよう公式が勧めています。
- Web（WebGL）ビルドでは、`Cookie`・`Cookie2`・`User-Agent`はブラウザーが設定するため、独自の値を付けられない場合があります。

レスポンスヘッダーは`GetResponseHeader("Content-Type")`で1つずつ、`GetResponseHeaders()`で`Dictionary<string, string>`としてまとめて取れます。ただしWebビルドで別ドメインのAPIを呼ぶ場合、読めるのはブラウザーが公開するヘッダーだけです。`Content-Type`などの基本的なもの以外は、サーバーが`Access-Control-Expose-Headers`で公開を許可している必要があります（[Fetch Standard](https://fetch.spec.whatwg.org/#concept-filtered-response-cors)）。

## timeoutの仕様：単位は秒・既定0は無制限

[timeoutプロパティ](https://docs.unity3d.com/6000.3/Documentation/ScriptReference/Networking.UnityWebRequest-timeout.html)は、応答が無いまま指定秒数が過ぎたら通信を打ち切る設定です。既定値は0で、0は「タイムアウトを適用せず応答まで待つ」を意味します。モバイル回線では応答が返らないまま待ち続けることがあるため、ゲームの通信には必ず値を入れてください。時間切れのときは`request.error`に`Request timeout`が入ります。

ソース（UnityWebRequest.bindings.cs）を読むと、実装上の注意点が3つあります。

- **整数の秒しか指定できない**：型はintで、内部では`値×1000`ミリ秒に変換されます。0.5秒のような指定はできません。
- **送信後は変更できない**：`SendWebRequest()`の後に代入すると`InvalidOperationException`（cannot modify the timeout）が出ます。
- **負の値は0になる**：`Math.Max(value, 0)`で丸められ、タイムアウト無しと同じ扱いになります。

何秒にするかは処理で変えます。ランキングやログイン情報のような小さいJSONは10秒前後、数MBのダウンロードはファイルの大きさと想定する回線速度から長めに取ります。`timeout`は通信全体を1つの値で区切るだけで、接続と受信を別々に制限する設定はありません。大きなファイルで「止まったまま」を見張りたいときは、`request.downloadProgress`や`downloadedBytes`の増え方をコルーチン側で監視し、一定時間増えなければ`Abort()`します。

## エラー判定とリトライ：resultの5値とresponseCode

### resultの5値による成功・失敗の分岐

`result`プロパティはUnity 2020.1で追加され、それ以前の`isNetworkError`・`isHttpError`はUnity 2020.2でObsoleteになりました。古い記事のコードは、次の表のとおり置き換えます。

| result              | 状態           | 例               | 旧プロパティ         |
| ------------------- | ------------ | --------------- | -------------- |
| InProgress          | 送信中          | 完了前             | –              |
| Success             | 成功           | 200番台           | –              |
| ConnectionError     | サーバーと通信できない  | 接続不可・TLS失敗・時間切れ | isNetworkError |
| ProtocolError       | サーバーがエラーを返した | 404・500         | isHttpError    |
| DataProcessingError | 受信データを処理できない | 壊れた画像の変換失敗      | –              |

定義は[UnityWebRequest.Resultのリファレンス](https://docs.unity3d.com/6000.3/Documentation/ScriptReference/Networking.UnityWebRequest.Result.html)にあります。`ProtocolError`のときもサーバーが返した本文は`downloadHandler.text`で読めるので、APIのエラーメッセージ（JSON）をログに残せます。

### responseCodeが-1や0になるとき

`responseCode`はサーバーが返したHTTPステータス（200・404・500など）で、リダイレクトを挟んだ場合は最後の応答の値です。[公式リファレンス](https://docs.unity3d.com/6000.3/Documentation/ScriptReference/Networking.UnityWebRequest-responseCode.html)は、まだ応答を処理していないときの値を-1と説明しています。送信が完了した後も-1や0のままなら、有効なHTTPステータスを取得できていない（接続できない・時間切れ・HTTP接続の禁止など）と考えて、`result`と`error`の文言で原因を見ます。成否を`responseCode == 200`で判定するのも避けます。201や204のような成功を失敗と誤認し、受信データの変換に失敗した`DataProcessingError`も見分けられないためです。

### リトライ対象の判断：接続失敗・408・429・一時的な5xx

UnityWebRequestには自動の再試行がありません。再試行の候補にするのは、時間をおけば回復しうる失敗です。`ConnectionError`のうち回線断や時間切れ、408（タイムアウト）・429（リクエスト過多）・500・502・503・504がこれに当たります。同じ`ConnectionError`でもTLSの確立失敗やHTTP接続の禁止は設定の問題なので、待っても直りません。400・401や、501（未実装）・505（HTTPバージョン非対応）も送り直しでは結果が変わりません。最終的には、呼び出すAPIの仕様と、同じ要求を2回処理しても問題ないか（冪等性）で決めます。待ち時間は倍々に増やし、多数の端末が同じ瞬間に再送しないよう揺らぎを足します。429や503で`Retry-After`ヘッダーが返ったときは、その秒数より前に送り直さないのが原則です（[RFC 9110](https://www.rfc-editor.org/rfc/rfc9110.html#section-10.2.3)）。次のコードはその解析を省いています。

```
IEnumerator GetWithRetry(string url, int maxAttempts = 3)
{
    for (int attempt = 1; attempt <= maxAttempts; attempt++)
    {
        using (UnityWebRequest request = UnityWebRequest.Get(url))
        {
            request.timeout = 10;
            yield return request.SendWebRequest();

            if (request.result == UnityWebRequest.Result.Success)
            {
                Debug.Log(request.downloadHandler.text);
                yield break;
            }

            // 一時的な失敗だけを再試行する。400・401・501などは送り直しても結果が変わらない
            long code = request.responseCode;
            bool retryable = request.result == UnityWebRequest.Result.ConnectionError
                || code == 408 || code == 429
                || code == 500 || code == 502 || code == 503 || code == 504;
            if (!retryable || attempt == maxAttempts)
            {
                Debug.LogError($"失敗: {request.responseCode} {request.error}");
                yield break;
            }
        }
        // 既定の3回なら待機は1秒・2秒の2回。倍々の待ち時間に揺らぎを足す
        float wait = Mathf.Pow(2, attempt - 1) + UnityEngine.Random.Range(0f, 0.5f);
        yield return new WaitForSecondsRealtime(wait); // timeScale=0でも止まらない
    }
}
```

1回ごとに新しいUnityWebRequestを作り直している点に注意してください。送信済みのインスタンスは再送できません。待機に`WaitForSecondsRealtime`を使うのは、`Time.timeScale`を0にしてゲームを一時停止しても再試行の待ちが止まらないようにするためです。指数バックオフと再試行回数の上限の決め方は[リトライパターンとは？指数バックオフ・ジッター・リトライ予算の実装判断を解説](/tech/details/16185/)で詳しく扱っています。POSTで再送するときは、サーバー側で同じリクエストを二重に処理しない仕組み（リクエストIDなど）が必要です。

## async/awaitでの記述：Unity 2023.1以降のAwaitable

Unity 2023.1で`AsyncOperation`に`GetAwaiter()`拡張メソッドが追加されました（UnityCsReferenceの`Awaitable.AsyncOperation.cs`）。`SendWebRequest()`の戻り値`UnityWebRequestAsyncOperation`はAsyncOperationを継承しているので、Unity 6ではUniTaskなどの外部ライブラリ無しで`await`できます。

```
using System;
using System.Threading;
using UnityEngine;
using UnityEngine.Networking;

public class AwaitSample : MonoBehaviour
{
    async void Start()
    {
        try
        {
            string text = await GetTextAsync("https://example.com/api/score", destroyCancellationToken);
            Debug.Log(text);
        }
        catch (OperationCanceledException)
        {
            Debug.Log("オブジェクト破棄で中断");
        }
        catch (Exception e)
        {
            Debug.LogError(e.Message);
        }
    }

    static async Awaitable<string> GetTextAsync(string url, CancellationToken token)
    {
        using UnityWebRequest request = UnityWebRequest.Get(url);
        request.timeout = 10;
        // トークンが取り消されたら通信そのものを止める
        using (token.Register(() => request.Abort()))
        {
            await request.SendWebRequest();
        }
        token.ThrowIfCancellationRequested();

        if (request.result != UnityWebRequest.Result.Success)
            throw new Exception($"{request.result} {request.responseCode} {request.error}");
        return request.downloadHandler.text;
    }
}
```

このコードでは、オブジェクトが破棄されたときに取り消される`destroyCancellationToken`を渡し、取り消されたら`request.Abort()`で通信そのものを止めています。`Awaitable.FromAsyncOperation(op, token)`にトークンを渡す書き方もありますが、こちらは待つのをやめるだけで、通信が止まるかどうかはソースから読み取れません。通信を確実に止めたいときは`Abort()`を呼んでください。

結果を呼び出し元に返したい・try/catchで失敗をまとめて扱いたいならawait、既存コードがコルーチンで統一されているならコルーチンのままで構いません。`async void`は例外が呼び出し元に伝わらないので、`Start`のような入口だけに使い、中では必ずtry/catchで受けます。

## 画像・音声・AssetBundleの取得API

画像は`UnityWebRequestTexture.GetTexture`で取得します。[公式リファレンス](https://docs.unity3d.com/6000.0/Documentation/ScriptReference/Networking.UnityWebRequestTexture.GetTexture.html)によると、テクスチャへの変換はワーカースレッドで行われるため、`Texture2D.LoadImage`で自前変換するよりメインスレッドの負荷を抑えられます。

```
using (UnityWebRequest request = UnityWebRequestTexture.GetTexture("https://example.com/icon.png"))
{
    yield return request.SendWebRequest();
    if (request.result != UnityWebRequest.Result.Success)
    {
        Debug.LogError(request.error);
        yield break;
    }
    Texture2D tex = DownloadHandlerTexture.GetContent(request);
    GetComponent<Renderer>().material.mainTexture = tex;
}
```

音声は`UnityWebRequestMultimedia.GetAudioClip(url, AudioType.MPEG)`のように形式を指定し、`DownloadHandlerAudioClip.GetContent(request)`で`AudioClip`を取り出します。AssetBundleは`UnityWebRequestAssetBundle.GetAssetBundle(url)`と`DownloadHandlerAssetBundle.GetContent(request)`の組み合わせです。古い記事にある`UnityWebRequest.GetTexture`・`UnityWebRequest.GetAssetBundle`はエラー扱いのObsoleteで、Unity 6ではコンパイルが通りません。

## HTTP接続・WebGL・Disposeで失敗する原因と対処

### 「Insecure connection not allowed」の対処：HTTPS化と開発時のHTTP許可

Unity 2022.1から、UnityWebRequestは既定でHTTPSだけを許可します。`http://`のURLに送ると`InvalidOperationException: Insecure connection not allowed`が出ます。設定は**Edit > Project Settings > Player > Other Settings**の**Allow downloads over HTTP**で、選択肢は3つです（[InsecureHttpOption](https://docs.unity.com/en-us/engine/6000.7/script-reference/unityeditor/insecurehttpoption)）。

| 設定値                           | HTTP接続    | 使いどころ              |
| ----------------------------- | --------- | ------------------ |
| Not Allowed（既定）               | 禁止        | 本番ビルド              |
| Allowed in Development Builds | 開発ビルドだけ許可 | HTTPの検証サーバーを使う間    |
| Always Allowed                | 常に許可      | HTTPSにできない接続先が残る場合 |

この設定はすべてのプラットフォームに効き、ビルド時の値が使われます。実行中には変えられません。`localhost`と`127.0.0.1`への接続は設定に関係なく許可されるので、手元の開発サーバーはNot Allowedのままでも呼べます。Always Allowedは通信内容が平文で流れるため、本番では接続先をHTTPSにするのが先です。

### WebGL（Web）通信におけるCORSとFetch APIの制約

[Web networkingのマニュアル](https://docs.unity3d.com/6000.3/Documentation/Manual/webgl-networking.html)によると、WebビルドのUnityWebRequestはブラウザーのFetch APIで実装されています。そのため、ゲームを置いたドメインと別のサーバーへ送るには、相手サーバーがCORSで許可している必要があります。エディタでは動くのにWebGLビルドだけ失敗し、ブラウザーのコンソールに「Cross-Origin Request Blocked」が出ていれば、原因はUnity側ではなくサーバーの`Access-Control-Allow-Origin`などの応答ヘッダーです。設定方法は[CORSとは？仕組み・プリフライト・サーバー設定例・エラー解決を実装目線で解説](/tech/details/13763/)を参照してください。

Webでは他にも次の制約があります。

- `while (!request.isDone) {}`のように完了を同期で待つコードは使えない（ブラウザーが処理を止める）。コルーチンかawaitで待つ。
- `System.Net.Http`（HttpClient）や`System.Net.Sockets`は使えない。Webで通信するならUnityWebRequestを直接使う。
- `CertificateHandler`による証明書の独自検証は対象外（Android・iOS・デスクトップなどに限られる）。

WebGLビルドの手順や対応ブラウザーは[WebGLとは？仕組み・対応ブラウザの確認・三角形を描くコードとUnityビルドまで解説](/tech/details/4884/)にまとめています。

### Disposeによるネイティブ資源とハンドラーの解放

UnityWebRequestはネイティブ（C++側）の資源を持っています。[公式リファレンス](https://docs.unity3d.com/6000.3/Documentation/ScriptReference/Networking.UnityWebRequest.Dispose.html)は、成功・失敗にかかわらず使い終わったら`Dispose`を呼ぶこと、例外が出ても確実に片付くよう`using`文を使うことを勧めています。`disposeDownloadHandlerOnDispose`・`disposeUploadHandlerOnDispose`・`disposeCertificateHandlerOnDispose`の既定値はいずれもtrueで、本体を破棄すればハンドラーも一緒に破棄されます。ハンドラーを使い回すためにこれらをfalseにしたときは、ハンドラーの`Dispose`を自分で呼びます。

## 旧WWWクラスからUnityWebRequestへの移行対応表

WWWクラスはUnity 6（6000.6.0f1のソース）でも残っていますが、「Use UnityWebRequest, a fully featured replacement which is more efficient and has additional features」というObsolete属性が付いています。ソースを読むと、現在のWWWは内部でUnityWebRequestを作って送信するだけのラッパーです。新しく書くコードでは使わず、残っている箇所は次の対応表で置き換えます。

| WWW                      | UnityWebRequest                              |
| ------------------------ | -------------------------------------------- |
| `new WWW(url)`           | `UnityWebRequest.Get(url)`                   |
| `new WWW(url, form)`     | `UnityWebRequest.Post(url, form)`            |
| `yield return www`       | `yield return request.SendWebRequest()`      |
| `www.error`              | `request.result`と`request.error`             |
| `www.text` ／ `www.bytes` | `request.downloadHandler.text` ／ `.data`     |
| `www.texture`（生成）        | `UnityWebRequestTexture.GetTexture(url)`     |
| `www.texture`（取り出し）      | `DownloadHandlerTexture.GetContent(request)` |
| `WWW.EscapeURL(s)`       | `UnityWebRequest.EscapeURL(s)`               |

```
// 旧：WWW（Obsolete）
WWW www = new WWW(url);
yield return www;
if (string.IsNullOrEmpty(www.error)) Debug.Log(www.text);

// 新：UnityWebRequest
using (UnityWebRequest request = UnityWebRequest.Get(url))
{
    yield return request.SendWebRequest();
    if (request.result == UnityWebRequest.Result.Success) Debug.Log(request.downloadHandler.text);
}
```

移行で抜けやすいのは2点です。WWWを使った古いコードはDisposeを書いていないことが多く、置き換えた後も`using`を付け忘れること。そして、WWWのコンストラクターは生成した時点で通信を始めますが、UnityWebRequestは`SendWebRequest()`を呼ぶまで何も送らないことです。`timeout`やヘッダーは、その間に設定します。

## HttpClientとの使い分け：Unity 6.5のUnityHttpMessageHandler

Unity 6.5（6000.5）で[UnityHttpMessageHandler](https://docs.unity.com/en-us/engine/6000.7/script-reference/unityengine/networking/unityhttpmessagehandler)が追加されました。UnityCsReferenceでは6000.4.0f1に無く、6000.5.0f1から存在します。.NET標準の`HttpClient`に渡すと、HttpClientの送受信をUnityWebRequestが担うようになります。

```
using System.Net.Http;
using UnityEngine;
using UnityEngine.Networking;

public class HttpClientSample : MonoBehaviour
{
    async void Start()
    {
        try
        {
            Debug.Log(await GetAsync("https://example.com/api/score"));
        }
        catch (HttpRequestException e)
        {
            Debug.LogError(e.Message);
        }
    }

    static async Awaitable<string> GetAsync(string url)
    {
        // Unity 6.5（6000.5）以降。HttpClientの下回りをUnityWebRequestにする
        using var client = new HttpClient(new UnityHttpMessageHandler());
        return await client.GetStringAsync(url);
    }
}
```

このクラスが役に立つのは、HttpClientを前提にしたライブラリをUnityで使う場面です。公式の説明では、gRPCの`GrpcChannel`の`HttpHandler`に渡す使い方が例示されています。判断の基準は次のとおりです。

- **自分でREST APIを呼ぶだけ**なら、UnityWebRequestを直接使う。Web（WebGL）でも同じコードで動き、この記事のサンプルがそのまま使える。
- **HttpClient前提のSDKやgRPCクライアントを入れたい**なら、Unity 6.5以降でUnityHttpMessageHandlerを渡す。Unity 6.3 LTSを使い続けるプロジェクトでは使えない。
- HttpClientをハンドラー無しで使う書き方は、Webビルドで動かないので避ける。

通信まわりの実装をゲームごとに作り直さず、サーバー側のAPI設計から含めて固めたい場合は、[ゲームアプリ開発](https://www.issoh.co.jp/service/apps/game/)の支援内容も参考にしてください。

## よくある質問

### UnityWebRequestにタイムアウトはありますか？

あります。`request.timeout = 10;`のように秒数（int）で指定します。既定値の0は時間制限なしです。`SendWebRequest()`の後には変更できないので、送信前に設定してください。

### UnityWebRequest.PostでJSONを送るにはどうすればよいですか？

Unity 2022.2以降は`UnityWebRequest.Post(url, json, "application/json")`を使います。2022.1以前は`new UnityWebRequest(url, "POST")`に`UploadHandlerRaw`と`DownloadHandlerBuffer`を付け、Content-Typeヘッダーを設定します。引数2つの`Post(url, json)`はURLエンコードされるため使いません。

### WWWクラスはまだ使えますか？

Unity 6でもクラス自体は残っていますが、Obsoleteで警告が出ます。公式はUnityWebRequestを完全な置き換えとしているので、移行してください。`WWW.EscapeURL`も`UnityWebRequest.EscapeURL`に置き換えられます。

### responseCodeが0や-1になるのはなぜですか？

HTTPの応答を受け取れていないためです。公式は応答を処理する前の値を-1と説明しています。接続できない・時間切れ・HTTP接続の禁止などが原因なので、`result`が`ConnectionError`かどうかと`error`の文言を確認します。

### WebGLビルドだけ通信が失敗するのはなぜですか？

WebではUnityWebRequestがブラウザーのFetch APIで動くため、別ドメインのサーバーにはCORSの許可が必要です。ブラウザーのコンソールにCORSのエラーが出ていれば、サーバー側に`Access-Control-Allow-Origin`などの応答ヘッダーを追加します。

## 関連記事

- [Unity 6.3 LTS（6000.3）の新機能と変更点｜最新パッチ・サポート期限・6.0からの移行](/tech/details/10417/)
- [WebGLとは？仕組み・対応ブラウザの確認・三角形を描くコードとUnityビルドまで解説](/tech/details/4884/)
- [CORSとは？仕組み・プリフライト・サーバー設定例・エラー解決を実装目線で解説](/tech/details/13763/)
- [リトライパターンとは？指数バックオフ・ジッター・リトライ予算の実装判断を解説](/tech/details/16185/)

---

出典: [UnityWebRequestの使い方｜GET・POST(JSON)・timeout・エラー処理とawait【Unity 6対応】](<https://www.issoh.co.jp/tech/details/6891/>)（株式会社一創）
