🛰️ UnityWebRequest 实践指南

🛰️ UnityWebRequest 实践指南

在 Unity 中进行网络通信,首选方案早已从 WWW 进化为 UnityWebRequest。它功能强大,支持 GET/POST/PUT/DELETE、文件上传/下载、JSON交互、证书校验、断点续传……一站式搞定。


🌐0.HTTP方法

方法用途简述是否携带数据幂等性典型用途示例
GET 获取数据 ❌ 否 ✅ 是 获取用户、商品详情
POST 新增资源 / 提交数据 ✅ 是 ❌ 否 创建订单、上传表单
PUT 更新整个资源 ✅ 是 ✅ 是 修改用户信息
DELETE 删除资源 ❌/✅ 可选 ✅ 是 删除用户、移除记录

Unity开发中使用场景

类型示例
GET 拉配置 / 拉资源列表 / 请求状态
POST 登录 / 注册 / 提交反馈 / 创建记录
PUT 更新设置 / 修改用户信息
DELETE 删除角色 / 移除物品 / 清除缓存

🔹 1. 基础:GET 请求

 
 
 
xxxxxxxxxx
 
 
 
 
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;

public class SimpleGet : MonoBehaviour
{
    void Start()
    {
        StartCoroutine(GetData());
    }

    IEnumerator GetData()
    {
        UnityWebRequest req = UnityWebRequest.Get("https://jsonplaceholder.typicode.com/posts/1");
        yield return req.SendWebRequest();

        if (req.result == UnityWebRequest.Result.Success)
        {
            Debug.Log(req.downloadHandler.text);
        }
        else
        {
            Debug.LogError(req.error);
        }
    }
}
 

🔹 2. POST 请求(发送 JSON 数据)

 
 
 
xxxxxxxxxx
 
 
 
 
IEnumerator PostJson()
{
    string json = JsonUtility.ToJson(new { username = "伯爵", score = 999 });

    var request = new UnityWebRequest("https://example.com/api/post", "POST");
    byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(json);

    request.uploadHandler = new UploadHandlerRaw(bodyRaw);
    request.downloadHandler = new DownloadHandlerBuffer();
    request.SetRequestHeader("Content-Type", "application/json");

    yield return request.SendWebRequest();

    if (request.result == UnityWebRequest.Result.Success)
    {
        Debug.Log("Response: " + request.downloadHandler.text);
    }
    else
    {
        Debug.LogError("Error: " + request.error);
    }
}
 

🌀 3. Modern 写法:async/await + UniTask(推荐)

用 UniTask 改写更现代,更易管理任务链和错误:

 
 
 
xxxxxxxxxx
 
 
 
 
using Cysharp.Threading.Tasks;
using UnityEngine.Networking;

public async UniTask<string> FetchAsync(string url)
{
    using var request = UnityWebRequest.Get(url);
    await request.SendWebRequest();

    if (request.result == UnityWebRequest.Result.Success)
        return request.downloadHandler.text;
    else
        throw new System.Exception($"网络失败: {request.error}");
}
 

调用方式:

 
 
 
xxxxxxxxxx
 
 
 
 
private async UniTaskVoid Start()
{
    try
    {
        string content = await FetchAsync("https://api.github.com/users/unity3d");
        Debug.Log("✅ Success: " + content);
    }
    catch (System.Exception ex)
    {
        Debug.LogError("❌ 网络错误: " + ex.Message);
    }
}
 

⚠️ 4. 常见问题与陷阱

问题原因解决方案
request.error == "Unknown Error" HTTPS 请求失败 目标站未支持 TLS 或证书问题
中文乱码 编码问题 Encoding.UTF8.GetString() 手动解码
CORS 跨域问题(WebGL) 浏览器安全限制 需要服务器设置 Access-Control-Allow-Origin
Post Json 无响应 没加头 别忘了加 Content-Type: application/json

🔐 5. 自定义请求头(Header)

 
 
 
xxxxxxxxxx
 
 
 
 
request.SetRequestHeader("Authorization", "Bearer xxx-token-abc");
request.SetRequestHeader("X-User-ID", "elegant-vampire");
 

🧪 6. Json 响应结构解析

 
 
 
xxxxxxxxxx
 
 
 
 
[System.Serializable]
public class User
{
    public string name;
    public int age;
}

User user = JsonUtility.FromJson<User>(json);
 

💡 Unity 内置 JsonUtility 不支持复杂嵌套,如字典、数组对象。可使用 Newtonsoft.JsonUtf8Json 替代。


📥 7. 下载文件到本地磁盘

 
 
 
xxxxxxxxxx
 
 
 
 
IEnumerator DownloadFile(string url, string localPath)
{
    UnityWebRequest request = UnityWebRequest.Get(url);
    request.downloadHandler = new DownloadHandlerFile(localPath); // ✅ 直接写入磁盘
    yield return request.SendWebRequest();

    if (request.result == UnityWebRequest.Result.Success)
    {
        Debug.Log("文件保存至: " + localPath);
    }
    else
    {
        Debug.LogError("下载失败: " + request.error);
    }
}
 

🌐 8. 实践场景总结

场景推荐方式
查询接口数据 UnityWebRequest.Get() + JsonUtility
上传分数/状态 UnityWebRequest.Post() + Json
下载资源文件 DownloadHandlerFile
WebGL 通信 需处理 CORS 和证书问题
请求队列管理 使用 UniTask + 自定义任务队列

9.断点续传示例

功能实现方式
支持中断恢复 使用 UnityWebRequest + HTTP Range 请求
保存已下载数据 写入本地临时 .part 文件
合并为完整文件 下载完成后重命名为最终文件
 
 
 
xxxxxxxxxx
 
 
 
 
using System;
using System.IO;
using System.Threading;
using UnityEngine;
using UnityEngine.Networking;
using Cysharp.Threading.Tasks;

public class ResumeDownloader : MonoBehaviour
{
    public string downloadUrl = "https://example.com/bigfile.zip";
    public string savePath = "bigfile.zip";

    private string TempPath => savePath + ".part";

    public async UniTaskVoid StartDownload()
    {
        long existingSize = 0;

        if (File.Exists(TempPath))
            existingSize = new FileInfo(TempPath).Length;

        using var request = UnityWebRequest.Get(downloadUrl);
        request.SetRequestHeader("Range", $"bytes={existingSize}-");

        string directory = Path.GetDirectoryName(savePath);
        if (!Directory.Exists(directory))
            Directory.CreateDirectory(directory);

        request.downloadHandler = new DownloadHandlerBuffer();
        var op = request.SendWebRequest();

        await UniTask.WaitUntil(() => op.isDone, cancellationToken: this.GetCancellationTokenOnDestroy());

        if (request.result != UnityWebRequest.Result.Success && request.responseCode != 206 && request.responseCode != 200)
        {
            Debug.LogError($"❌ 下载失败: {request.error}");
            return;
        }

        var data = request.downloadHandler.data;

        // 追加写入到 .part 文件
        using (var fs = new FileStream(TempPath, FileMode.Append, FileAccess.Write))
        {
            fs.Write(data, 0, data.Length);
        }

        Debug.Log($"✅ 下载分块成功,大小: {data.Length} 字节");

        long totalSize = 0;
        if (request.GetResponseHeader("Content-Range") is string range)
        {
            // Content-Range: bytes 1000-4999/12345
            var totalMatch = System.Text.RegularExpressions.Regex.Match(range, @"/(\d+)");
            if (totalMatch.Success)
                totalSize = long.Parse(totalMatch.Groups[1].Value);
        }

        long currentSize = new FileInfo(TempPath).Length;

        Debug.Log($"🧪 已下载: {currentSize} / {totalSize}");

        if (currentSize >= totalSize)
        {
            File.Move(TempPath, savePath, true);
            Debug.Log($"🎉 下载完成,文件保存在: {savePath}");
        }
        else
        {
            Debug.Log("⏸️ 下载未完成,可稍后继续");
        }
    }

    [ContextMenu("断点续传下载测试")]
    public void StartTest()
    {
        StartDownload().Forget();
    }
}
 

✅ 面试题推荐(附解析)

  1. UnityWebRequest 与 WWW 的区别? → UnityWebRequest 是现代 API,支持更多功能、更安全,已取代 WWW。
  2. 如何判断 UnityWebRequest 请求成功? → 使用 request.result == UnityWebRequest.Result.Success(Unity 2020.1 起推荐)
  3. 如何解决 WebGL 网络请求跨域失败? → 需要服务端设置 CORS 响应头,如 Access-Control-Allow-Origin: *
  4. JsonUtility 无法解析复杂结构怎么办? → 使用 Newtonsoft.JsonSystem.Text.Json 替代。
  5. async/await 和 Coroutine 区别? async/await 结构清晰,可组合任务、异常处理好;Coroutine 更适合场景逻辑协程。
posted @ 2025-08-08 09:15  世纪末の魔术师  阅读(265)  评论(0)    收藏  举报