using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using DefaultNamespace;
using Cysharp.Threading.Tasks;
using Newtonsoft.Json;
namespace WXGame.Network
{
///
/// WebRequest管理器 - 简化版HTTP请求管理器
/// 支持GET、POST请求,自动处理token认证
///
public class WebRequestManager : MonoBehaviour
{
#region 单例模式
private static WebRequestManager _instance;
public static WebRequestManager Instance
{
get
{
if (_instance == null)
{
// 查找场景中是否已存在实例
_instance = FindObjectOfType();
if (_instance == null)
{
// 创建新的GameObject并添加组件
GameObject go = new GameObject("WebRequestManager");
_instance = go.AddComponent();
DontDestroyOnLoad(go); // 保持跨场景存在
Debug.Log("WebRequestManager: 创建新的WebRequestManager实例");
}
}
return _instance;
}
}
#endregion
#region 配置参数
[Header("网络请求配置")] [SerializeField] private float defaultTimeout = 30f; // 默认超时时间(秒)
[SerializeField] private int maxRetryCount = 3; // 最大重试次数
[SerializeField] private float retryDelay = 2f; // 重试延迟时间(秒)
[SerializeField] private int maxConcurrentRequests = 5; // 最大并发请求数
[Header("调试设置")] [SerializeField] private bool enableLogging = true; // 是否启用日志
[SerializeField] private bool enableVerboseLogging = false; // 是否启用详细日志
[Header("图片下载配置")] [SerializeField] private float imageDownloadTimeout = 10f; // 图片下载超时时间(秒)
#endregion
#region 私有字段
private Queue requestQueue = new Queue(); // 请求队列
private List activeRequests = new List(); // 活跃请求列表
private Dictionary defaultHeaders = new Dictionary(); // 默认请求头
// 图片下载相关字段
private Dictionary imageCache = new Dictionary(); // 简单图片缓存
#endregion
#region 事件定义
///
/// 请求完成事件
///
public event Action OnRequestCompleted;
///
/// 请求失败事件
///
public event Action OnRequestFailed;
///
/// 网络错误事件
///
public event Action OnNetworkError;
///
/// 图片下载完成事件
///
public event Action OnImageDownloadCompleted;
///
/// 图片下载失败事件
///
public event Action OnImageDownloadFailed;
#endregion
#region Unity生命周期
private void Awake()
{
// 确保单例模式正确实现
if (_instance == null)
{
_instance = this;
DontDestroyOnLoad(gameObject);
InitializeManager();
Debug.Log("WebRequestManager: 初始化完成");
}
else if (_instance != this)
{
Debug.LogWarning("WebRequestManager: 检测到重复实例,销毁当前对象");
Destroy(gameObject);
}
}
private void OnDestroy()
{
if (_instance == this)
{
_instance = null;
Debug.Log("WebRequestManager: 实例已销毁");
}
}
#endregion
#region 初始化方法
///
/// 初始化管理器
///
private void InitializeManager()
{
// 设置默认请求头(不设置Content-Type,让每个请求自己决定)
defaultHeaders["Accept"] = "application/json";
LogMessage("WebRequestManager初始化完成", LogType.Log);
}
///
/// 设置默认请求头
///
/// 请求头键
/// 请求头值
public void SetDefaultHeader(string key, string value)
{
if (string.IsNullOrEmpty(key))
{
LogMessage("设置默认请求头失败:键不能为空", LogType.Warning);
return;
}
defaultHeaders[key] = value;
LogMessage($"设置默认请求头: {key} = {value}", LogType.Log);
}
///
/// 清除默认请求头
///
public void ClearDefaultHeaders()
{
defaultHeaders.Clear();
LogMessage("已清除所有默认请求头", LogType.Log);
}
#endregion
#region 日志方法
///
/// 记录日志消息
///
/// 日志消息
/// 日志类型
private void LogMessage(string message, LogType logType = LogType.Log)
{
if (!enableLogging) return;
string prefix = "[WebRequestManager] ";
switch (logType)
{
case LogType.Log:
Debug.Log(prefix + message);
break;
case LogType.Warning:
Debug.LogWarning(prefix + message);
break;
case LogType.Error:
Debug.LogError(prefix + message);
break;
}
}
///
/// 记录详细日志(仅在启用详细日志时输出)
///
/// 详细日志消息
private void LogVerbose(string message)
{
if (enableVerboseLogging)
{
LogMessage($"[详细] {message}", LogType.Log);
}
}
#endregion
#region 辅助方法
///
/// 转换参数值为字符串
///
/// 参数值
/// 字符串值
private string ConvertParameterValue(object value)
{
if (value == null)
{
return string.Empty;
}
// 处理数组类型
if (value.GetType().IsArray)
{
return JsonUtility.ToJson(value);
}
// 处理列表类型
if (value is System.Collections.IList)
{
return JsonUtility.ToJson(value);
}
// 处理其他对象类型
if (value.GetType().IsClass && value.GetType() != typeof(string))
{
return JsonUtility.ToJson(value);
}
// 处理基本类型
return value.ToString();
}
#endregion
#region 公共接口方法
///
/// 发送GET请求(协程方式)
///
/// 请求URL
/// 完成回调
/// 错误回调
/// 是否启用sign签名
/// 签名参数(当enableSign为true时使用)
public void GetRequest(string url, Action onComplete = null, Action onError = null,
bool enableSign = false, Dictionary signParams = null)
{
if (string.IsNullOrEmpty(url))
{
LogMessage("GET请求失败:URL不能为空", LogType.Error);
onError?.Invoke("URL不能为空");
return;
}
// 获取token并添加到请求头
var requestHeaders = new Dictionary();
string token = Apis.GetToken();
if (!string.IsNullOrEmpty(token))
{
requestHeaders["token"] = token;
LogMessage($"GET请求添加token: {token.Substring(0, Mathf.Min(10, token.Length))}...", LogType.Log);
}
// 处理sign签名
if (enableSign && signParams != null)
{
try
{
// 写死的appkey和appSecret
string appKey = "38kisezhasfgxhh98b";
string appSecret = "2d6wy8hm8rxbi4xt8dghovggdoodqs57";
// 创建完整的参数字典
var fullParams = new Dictionary(signParams);
// 添加固定的appkey
fullParams["appkey"] = appKey;
// 添加时间戳
fullParams["timestamp"] = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
// 生成sign签名(使用SignUtil,与test.cs保持一致)
string sign = SignUtil.GetSign(fullParams, appSecret);
if (!string.IsNullOrEmpty(sign))
{
fullParams["sign"] = sign;
LogMessage($"GET请求生成sign: {sign}", LogType.Log);
LogMessage($"GET请求签名原文: {SignUtil.LastPlainText}", LogType.Log);
// 将fullParams转换为URL查询参数
var queryParams = new List();
foreach (var kvp in fullParams)
{
queryParams.Add($"{UnityWebRequest.EscapeURL(kvp.Key)}={UnityWebRequest.EscapeURL(kvp.Value)}");
}
// 构建完整的URL
string queryString = string.Join("&", queryParams);
if (url.Contains("?"))
{
url += "&" + queryString;
}
else
{
url += "?" + queryString;
}
LogMessage($"GET请求完整URL: {url}", LogType.Log);
}
else
{
LogMessage("GET请求sign生成失败", LogType.Warning);
}
}
catch (Exception ex)
{
LogMessage($"GET请求sign生成异常: {ex.Message}", LogType.Error);
}
}
var task = new WebRequestTask
{
Url = url,
Method = "GET",
Headers = requestHeaders,
Timeout = defaultTimeout,
OnComplete = onComplete,
OnError = onError
};
EnqueueRequest(task);
}
///
/// 发送POST请求(自动处理token,使用JSON格式)
///
/// 请求URL
/// 完成回调
/// 错误回调
/// 是否启用sign签名
/// 签名参数(当enableSign为true时使用)
public void PostRequest(string url, Action onComplete = null, Action onError = null,
bool enableSign = true, Dictionary signParams = null)
{
if (string.IsNullOrEmpty(url))
{
LogMessage("POST请求失败:URL不能为空", LogType.Error);
onError?.Invoke("URL不能为空");
return;
}
// 获取token并添加到请求头
var requestHeaders = new Dictionary();
string token = Apis.GetToken();
if (!string.IsNullOrEmpty(token))
{
requestHeaders["token"] = token;
LogMessage($"POST请求添加token: {token.Substring(0, Mathf.Min(10, token.Length))}...", LogType.Log);
}
// 处理sign签名
if (enableSign)
{
try
{
// 写死的appkey和appSecret
string appKey = "38kisezhasfgxhh98b";
string appSecret = "2d6wy8hm8rxbi4xt8dghovggdoodqs57";
Dictionary fullParams = null;
// 创建完整的参数字典
if (signParams != null)
fullParams = new Dictionary(signParams);
else
{
fullParams = new Dictionary();
}
// 添加固定的appkey
fullParams["appkey"] = (string)appKey;
// 添加时间戳
fullParams["timestamp"] = (string)DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
// 生成sign签名(使用SignUtil,与test.cs保持一致)
string sign = SignUtil.GetSign(fullParams, appSecret);
if (!string.IsNullOrEmpty(sign))
{
fullParams["sign"] = (string)sign;
LogMessage($"POST请求生成sign: {sign}", LogType.Log);
LogMessage($"POST请求签名原文: {SignUtil.LastPlainText}", LogType.Log);
// 序列化为JSON(使用Newtonsoft.Json,与test.cs保持一致)
string data = JsonConvert.SerializeObject(fullParams);
string contentType = "application/json"; // 强制使用JSON格式
LogMessage($"POST请求最终JSON数据: {data}", LogType.Log);
var task1 = new WebRequestTask
{
Url = url,
Method = "POST",
Data = data,
ContentType = "application/json",
Headers = requestHeaders,
Timeout = defaultTimeout,
OnComplete = onComplete,
OnError = onError
};
EnqueueRequest(task1);
}
else
{
LogMessage("POST请求sign生成失败", LogType.Warning);
}
}
catch (Exception ex)
{
LogMessage($"POST请求sign生成异常: {ex.Message}", LogType.Error);
}
}
else
{
Debug.Log(JsonConvert.SerializeObject(signParams));
var task = new WebRequestTask
{
Url = url,
Method = "POST",
Data = enableSign && signParams != null ? JsonConvert.SerializeObject(signParams) : "",
ContentType = "application/json",
Headers = requestHeaders,
Timeout = defaultTimeout,
OnComplete = onComplete,
OnError = onError
};
EnqueueRequest(task);
}
}
///
/// 发送POST请求(自动处理token,使用JSON格式)
///
/// 请求URL
/// 完成回调
/// 错误回调
/// 是否启用sign签名
/// 签名参数(当enableSign为true时使用)
public void PostRequest(string url, Action onComplete = null, Action onError = null,bool enableSign = true)
{
if (string.IsNullOrEmpty(url))
{
LogMessage("POST请求失败:URL不能为空", LogType.Error);
onError?.Invoke("URL不能为空");
return;
}
// 获取token并添加到请求头
var requestHeaders = new Dictionary();
string token = Apis.GetToken();
if (!string.IsNullOrEmpty(token))
{
requestHeaders["token"] = token;
LogMessage($"POST请求添加token: {token.Substring(0, Mathf.Min(10, token.Length))}...", LogType.Log);
}
// 处理sign签名
if (enableSign)
{
try
{
// 写死的appkey和appSecret
string appKey = "38kisezhasfgxhh98b";
string appSecret = "2d6wy8hm8rxbi4xt8dghovggdoodqs57";
Dictionary fullParams = null;
// 创建完整的参数字典
fullParams = new Dictionary();
// 添加固定的appkey
fullParams["appkey"] = (string)appKey;
// 添加时间戳
fullParams["timestamp"] = (string)DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
// 生成sign签名(使用SignUtil,与test.cs保持一致)
string sign = SignUtil.GetSign(fullParams, appSecret);
if (!string.IsNullOrEmpty(sign))
{
fullParams["sign"] = (string)sign;
LogMessage($"POST请求生成sign: {sign}", LogType.Log);
LogMessage($"POST请求签名原文: {SignUtil.LastPlainText}", LogType.Log);
// 序列化为JSON(使用Newtonsoft.Json,与test.cs保持一致)
string data = JsonConvert.SerializeObject(fullParams);
string contentType = "application/json"; // 强制使用JSON格式
LogMessage($"POST请求最终JSON数据: {data}", LogType.Log);
var task1 = new WebRequestTask
{
Url = url,
Method = "POST",
Data = data,
ContentType = "application/json",
Headers = requestHeaders,
Timeout = defaultTimeout,
OnComplete = onComplete,
OnError = onError
};
EnqueueRequest(task1);
}
else
{
LogMessage("POST请求sign生成失败", LogType.Warning);
}
}
catch (Exception ex)
{
LogMessage($"POST请求sign生成异常: {ex.Message}", LogType.Error);
}
}
}
///
/// 发送POST请求(自动处理token,使用JSON格式)
///
/// 请求URL
/// 完成回调
/// 错误回调
/// 是否启用sign签名
/// 签名参数(当enableSign为true时使用)
public void PostRequest(string url, Action onComplete = null, Action onError = null,
bool enableSign = true, Dictionary signParams = null)
{
if (string.IsNullOrEmpty(url))
{
LogMessage("POST请求失败:URL不能为空", LogType.Error);
onError?.Invoke("URL不能为空");
return;
}
// 获取token并添加到请求头
var requestHeaders = new Dictionary();
string token = Apis.GetToken();
if (!string.IsNullOrEmpty(token))
{
requestHeaders["token"] = token;
LogMessage($"POST请求添加token: {token.Substring(0, Mathf.Min(10, token.Length))}...", LogType.Log);
}
// 处理sign签名
if (enableSign)
{
try
{
// 写死的appkey和appSecret
string appKey = "38kisezhasfgxhh98b";
string appSecret = "2d6wy8hm8rxbi4xt8dghovggdoodqs57";
Dictionary fullParams = null;
// 创建完整的参数字典
if (signParams != null)
fullParams = new Dictionary(signParams);
else
{
fullParams = new Dictionary();
}
// 添加固定的appkey
fullParams["appkey"] = appKey;
// 添加时间戳
fullParams["timestamp"] = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
// 生成sign签名(使用SignUtil,与test.cs保持一致)
string sign = SignUtil.GetSign(fullParams, appSecret);
if (!string.IsNullOrEmpty(sign))
{
fullParams["sign"] = sign;
LogMessage($"POST请求生成sign: {sign}", LogType.Log);
LogMessage($"POST请求签名原文: {SignUtil.LastPlainText}", LogType.Log);
// 序列化为JSON(使用Newtonsoft.Json,与test.cs保持一致)
string data = JsonConvert.SerializeObject(fullParams);
string contentType = "application/json"; // 强制使用JSON格式
LogMessage($"POST请求最终JSON数据: {data}", LogType.Log);
var task1 = new WebRequestTask
{
Url = url,
Method = "POST",
Data = data,
ContentType = "application/json",
Headers = requestHeaders,
Timeout = defaultTimeout,
OnComplete = onComplete,
OnError = onError
};
EnqueueRequest(task1);
}
else
{
LogMessage("POST请求sign生成失败", LogType.Warning);
}
}
catch (Exception ex)
{
LogMessage($"POST请求sign生成异常: {ex.Message}", LogType.Error);
}
}
else
{
Debug.Log(JsonConvert.SerializeObject(signParams));
var task = new WebRequestTask
{
Url = url,
Method = "POST",
Data = enableSign && signParams != null ? JsonConvert.SerializeObject(signParams) : "",
ContentType = "application/json",
Headers = requestHeaders,
Timeout = defaultTimeout,
OnComplete = onComplete,
OnError = onError
};
EnqueueRequest(task);
}
}
///
/// 发送POST请求(JSON数据)
///
/// 请求URL
/// JSON数据
/// 自定义请求头
/// 超时时间
/// 完成回调
/// 错误回调
public void PostJsonRequest(string url, string jsonData, Dictionary headers = null,
float timeout = -1, Action onComplete = null, Action onError = null)
{
// 直接使用UnityWebRequest发送,因为这是旧的方法
StartCoroutine(PostJsonCoroutine(url, jsonData, headers, timeout, onComplete, onError));
}
///
/// 发送POST请求(表单数据)
///
/// 请求URL
/// 表单数据
/// 自定义请求头
/// 超时时间
/// 完成回调
/// 错误回调
public void PostFormRequest(string url, Dictionary formData, Dictionary headers = null,
float timeout = -1, Action onComplete = null, Action onError = null)
{
string formString = "";
if (formData != null && formData.Count > 0)
{
var formList = new List();
foreach (var kvp in formData)
{
formList.Add($"{UnityWebRequest.EscapeURL(kvp.Key)}={UnityWebRequest.EscapeURL(kvp.Value)}");
}
formString = string.Join("&", formList);
}
// 直接使用UnityWebRequest发送,因为这是旧的方法
StartCoroutine(PostFormCoroutine(url, formString, headers, timeout, onComplete, onError));
}
#endregion
#region 协程方法(用于旧方法)
///
/// POST JSON请求协程
///
private IEnumerator PostJsonCoroutine(string url, string jsonData, Dictionary headers,
float timeout, Action onComplete, Action onError)
{
var request = new UnityWebRequest(url, "POST");
request.uploadHandler = new UploadHandlerRaw(System.Text.Encoding.UTF8.GetBytes(jsonData));
request.downloadHandler = new DownloadHandlerBuffer();
request.SetRequestHeader("Content-Type", "application/json; charset=utf-8");
// 添加自定义请求头
if (headers != null)
{
foreach (var header in headers)
{
request.SetRequestHeader(header.Key, header.Value);
}
}
// 添加token
string token = Apis.GetToken();
if (!string.IsNullOrEmpty(token))
{
request.SetRequestHeader("token", token);
}
// 设置超时
if (timeout > 0)
{
request.timeout = (int)timeout;
}
yield return request.SendWebRequest();
var result = new WebRequestResult
{
Url = url,
ResponseCode = request.responseCode,
ResponseText = request.downloadHandler.text,
IsSuccess = request.result == UnityWebRequest.Result.Success
};
if (request.result != UnityWebRequest.Result.Success)
{
LogMessage($"POST JSON请求失败: {request.error}", LogType.Error);
onError?.Invoke(request.error);
}
else
{
LogMessage($"POST JSON请求成功: {request.responseCode}", LogType.Log);
onComplete?.Invoke(result);
}
request.Dispose();
}
///
/// POST表单请求协程
///
private IEnumerator PostFormCoroutine(string url, string formData, Dictionary headers,
float timeout, Action onComplete, Action onError)
{
var request = new UnityWebRequest(url, "POST");
request.uploadHandler = new UploadHandlerRaw(System.Text.Encoding.UTF8.GetBytes(formData));
request.downloadHandler = new DownloadHandlerBuffer();
request.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded");
// 添加自定义请求头
if (headers != null)
{
foreach (var header in headers)
{
request.SetRequestHeader(header.Key, header.Value);
}
}
// 添加token
string token = Apis.GetToken();
if (!string.IsNullOrEmpty(token))
{
request.SetRequestHeader("token", token);
}
// 设置超时
if (timeout > 0)
{
request.timeout = (int)timeout;
}
yield return request.SendWebRequest();
var result = new WebRequestResult
{
Url = url,
ResponseCode = request.responseCode,
ResponseText = request.downloadHandler.text,
IsSuccess = request.result == UnityWebRequest.Result.Success
};
if (request.result != UnityWebRequest.Result.Success)
{
LogMessage($"POST表单请求失败: {request.error}", LogType.Error);
onError?.Invoke(request.error);
}
else
{
LogMessage($"POST表单请求成功: {request.responseCode}", LogType.Log);
onComplete?.Invoke(result);
}
request.Dispose();
}
#endregion
#region 异步方法(UniTask)
///
/// 发送GET请求(异步方式,返回UniTask)
///
/// 请求URL
/// 是否启用sign签名
/// 签名参数(当enableSign为true时使用)
/// UniTask
public async UniTask GetRequestAsync(string url, bool enableSign = false, Dictionary signParams = null)
{
var completionSource = new UniTaskCompletionSource();
GetRequest(url,
onComplete: (result) => { completionSource.TrySetResult(result); },
onError: (error) => { completionSource.TrySetException(new System.Exception(error)); },
enableSign: enableSign,
signParams: signParams
);
return await completionSource.Task;
}
///
/// 发送POST请求(异步方式,返回UniTask)
///
/// 请求URL
/// 是否启用sign签名
/// 签名参数(当enableSign为true时使用)
/// UniTask
public async UniTask PostRequestAsync(string url)
{
var completionSource = new UniTaskCompletionSource();
PostRequest(url,
onComplete: (result) => { completionSource.TrySetResult(result); },
onError: (error) =>
{
completionSource.TrySetException(new System.Exception(error));
},
enableSign: true
);
return await completionSource.Task;
}
///
/// 发送POST请求(异步方式,返回UniTask)
///
/// 请求URL
/// 是否启用sign签名
/// 签名参数(当enableSign为true时使用)
/// UniTask
public async UniTask PostRequestAsync(string url, Dictionary signParams = null)
{
var completionSource = new UniTaskCompletionSource();
PostRequest(url,
onComplete: (result) => { completionSource.TrySetResult(result); },
onError: (error) => { completionSource.TrySetException(new System.Exception(error)); },
signParams: signParams
);
return await completionSource.Task;
}
///
/// 发送POST请求(异步方式,返回UniTask)
///
/// 请求URL
/// 是否启用sign签名
/// 签名参数(当enableSign为true时使用)
/// UniTask
public async UniTask PostRequestAsync(string url, Dictionary signParams = null)
{
var completionSource = new UniTaskCompletionSource();
PostRequest(url,
onComplete: (result) => { completionSource.TrySetResult(result); },
onError: (error) => { completionSource.TrySetException(new System.Exception(error)); },
signParams: signParams
);
return await completionSource.Task;
}
// ///
// /// 发送POST请求(异步方式,返回UniTask,支持int类型参数)
// ///
// /// 请求URL
// /// 签名参数(int类型值会自动转换为string)
// /// UniTask
// public async UniTask PostRequestAsync(string url, Dictionary signParams = null)
// {
// // 将int类型的参数转换为string类型
// Dictionary stringParams = null;
// if (signParams != null)
// {
// stringParams = new Dictionary();
// foreach (var kvp in signParams)
// {
// stringParams[kvp.Key] = kvp.Value.ToString();
// }
// LogMessage($"将int参数转换为string: {stringParams.Count}个参数", LogType.Log);
// }
//
// // 调用支持string参数的方法
// return await PostRequestAsync(url, stringParams);
// }
// ///
// /// 发送POST请求(异步方式,返回UniTask,支持混合类型参数)
// ///
// /// 请求URL
// /// 签名参数(支持string、int、bool等混合类型,会自动转换为string)
// /// UniTask
// public async UniTask PostRequestAsync(string url, Dictionary signParams = null)
// {
// // 将object类型的参数转换为string类型
// Dictionary stringParams = null;
// if (signParams != null)
// {
// stringParams = new Dictionary();
// foreach (var kvp in signParams)
// {
// stringParams[kvp.Key] = ConvertParameterValue(kvp.Value);
// }
// LogMessage($"将混合类型参数转换为string: {stringParams.Count}个参数", LogType.Log);
// }
//
// // 调用支持string参数的方法
// return await PostRequestAsync(url, stringParams);
// }
///
/// 发送POST请求(JSON数据,异步方式,返回UniTask)
///
/// 请求URL
/// JSON数据
/// 自定义请求头
/// 超时时间
/// UniTask
public async UniTask PostJsonRequestAsync(string url, string jsonData, Dictionary headers = null, float timeout = -1)
{
var completionSource = new UniTaskCompletionSource();
PostJsonRequest(url, jsonData, headers, timeout,
onComplete: (result) => { completionSource.TrySetResult(result); },
onError: (error) => { completionSource.TrySetException(new System.Exception(error)); }
);
return await completionSource.Task;
}
///
/// 发送POST请求(表单数据,异步方式,返回UniTask)
///
/// 请求URL
/// 表单数据
/// 自定义请求头
/// 超时时间
/// UniTask
public async UniTask PostFormRequestAsync(string url, Dictionary formData, Dictionary headers = null, float timeout = -1)
{
var completionSource = new UniTaskCompletionSource();
PostFormRequest(url, formData, headers, timeout,
onComplete: (result) => { completionSource.TrySetResult(result); },
onError: (error) => { completionSource.TrySetException(new System.Exception(error)); }
);
return await completionSource.Task;
}
#endregion
#region 请求队列管理
///
/// 将请求加入队列
///
/// 请求任务
private void EnqueueRequest(WebRequestTask task)
{
requestQueue.Enqueue(task);
LogVerbose($"请求已加入队列: {task.Method} {task.Url}");
// 尝试处理队列中的请求
ProcessQueue();
}
///
/// 处理请求队列
///
private void ProcessQueue()
{
// 如果当前活跃请求数未达到最大限制,且队列中有待处理请求
while (activeRequests.Count < maxConcurrentRequests && requestQueue.Count > 0)
{
var task = requestQueue.Dequeue();
activeRequests.Add(task);
StartCoroutine(ExecuteRequest(task));
}
}
///
/// 执行请求
///
/// 请求任务
private IEnumerator ExecuteRequest(WebRequestTask task)
{
LogMessage($"开始执行请求: {task.Method} {task.Url}", LogType.Log);
using (UnityWebRequest request = CreateUnityWebRequest(task))
{
// 设置超时
request.timeout = (int)task.Timeout;
// 发送请求
var operation = request.SendWebRequest();
// 等待请求完成或超时
float startTime = Time.time;
while (!operation.isDone)
{
// 检查是否超时
if (Time.time - startTime > task.Timeout)
{
request.Abort();
LogMessage($"请求超时: {task.Method} {task.Url}", LogType.Warning);
HandleRequestError(task, "请求超时");
break;
}
yield return null;
}
// 处理请求结果
if (operation.isDone)
{
HandleRequestResult(task, request);
}
}
// 从活跃请求列表中移除
activeRequests.Remove(task);
// 继续处理队列
ProcessQueue();
}
///
/// 创建UnityWebRequest对象
///
/// 请求任务
/// UnityWebRequest对象
private UnityWebRequest CreateUnityWebRequest(WebRequestTask task)
{
UnityWebRequest request;
switch (task.Method.ToUpper())
{
case "GET":
request = UnityWebRequest.Get(task.Url);
break;
case "POST":
// 使用UnityWebRequest.Post发送字符串数据
request = new UnityWebRequest(task.Url, "POST");
// 设置请求数据
if (!string.IsNullOrEmpty(task.Data))
{
byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(task.Data);
request.uploadHandler = new UploadHandlerRaw(bodyRaw);
LogMessage($"POST请求数据: {task.Data}", LogType.Log);
}
// 设置Content-Type
if (!string.IsNullOrEmpty(task.ContentType))
{
request.SetRequestHeader("Content-Type", task.ContentType);
LogMessage($"设置Content-Type: {task.ContentType}", LogType.Log);
}
else
{
// 默认使用urlencoded
request.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded");
LogMessage("使用默认Content-Type: application/x-www-form-urlencoded", LogType.Log);
}
// 设置下载处理器
request.downloadHandler = new DownloadHandlerBuffer();
break;
default:
throw new ArgumentException($"不支持的HTTP方法: {task.Method}");
}
// 设置默认请求头
foreach (var header in defaultHeaders)
{
request.SetRequestHeader(header.Key, header.Value);
}
// 设置自定义请求头
foreach (var header in task.Headers)
{
request.SetRequestHeader(header.Key, header.Value);
}
return request;
}
///
/// 处理请求结果
///
/// 请求任务
/// UnityWebRequest对象
private void HandleRequestResult(WebRequestTask task, UnityWebRequest request)
{
var result = new WebRequestResult
{
Url = task.Url,
Method = task.Method,
ResponseCode = request.responseCode,
ResponseText = request.downloadHandler?.text ?? "",
ResponseHeaders = request.GetResponseHeader(""),
IsSuccess = request.result == UnityWebRequest.Result.Success,
ErrorMessage = request.error
};
if (result.IsSuccess)
{
LogMessage($"请求成功: {task.Method} {task.Url} (状态码: {result.ResponseCode})", LogType.Log);
task.OnComplete?.Invoke(result);
OnRequestCompleted?.Invoke(result);
}
else
{
LogMessage($"请求失败: {task.Method} {task.Url} - {request.error}", LogType.Error);
HandleRequestError(task, request.error);
}
}
///
/// 处理请求错误
///
/// 请求任务
/// 错误信息
private void HandleRequestError(WebRequestTask task, string error)
{
// 检查是否需要重试
if (task.RetryCount < maxRetryCount)
{
task.RetryCount++;
LogMessage($"准备重试请求 ({task.RetryCount}/{maxRetryCount}): {task.Method} {task.Url}", LogType.Warning);
// 延迟重试
StartCoroutine(DelayedRetry(task));
}
else
{
LogMessage($"请求最终失败: {task.Method} {task.Url} - {error}", LogType.Error);
task.OnError?.Invoke(error);
OnRequestFailed?.Invoke(new WebRequestResult
{
Url = task.Url,
Method = task.Method,
IsSuccess = false,
ErrorMessage = error
});
OnNetworkError?.Invoke(error);
}
}
///
/// 延迟重试
///
/// 请求任务
private IEnumerator DelayedRetry(WebRequestTask task)
{
yield return new WaitForSeconds(retryDelay);
EnqueueRequest(task);
}
#endregion
#region 公共工具方法
///
/// 取消所有请求
///
public void CancelAllRequests()
{
LogMessage("取消所有请求", LogType.Warning);
// 清空队列
requestQueue.Clear();
// 停止所有活跃请求
foreach (var task in activeRequests)
{
task.OnError?.Invoke("请求被取消");
}
activeRequests.Clear();
}
///
/// 获取当前活跃请求数
///
/// 活跃请求数量
public int GetActiveRequestCount()
{
return activeRequests.Count;
}
///
/// 获取队列中等待的请求数
///
/// 等待请求数量
public int GetQueuedRequestCount()
{
return requestQueue.Count;
}
#endregion
#region 图片下载功能
///
/// 异步下载图片(UniTask方式)
///
/// 图片URL
/// 超时时间(秒),-1使用默认值
/// 下载的纹理,失败返回null
public async UniTask DownloadImageAsync(string imageUrl, float timeout = -1)
{
UnityWebRequest request = null;
try
{
// 检查URL是否有效
if (string.IsNullOrEmpty(imageUrl))
{
LogMessage("图片URL为空", LogType.Warning);
return null;
}
LogMessage($"开始下载图片: {imageUrl}", LogType.Log);
// 检查缓存中是否已存在该图片
if (imageCache.ContainsKey(imageUrl))
{
LogMessage("从缓存中获取图片", LogType.Log);
return imageCache[imageUrl];
}
// 使用UnityWebRequest下载图片
request = UnityWebRequestTexture.GetTexture(imageUrl);
// 设置超时时间
float actualTimeout = timeout > 0 ? timeout : imageDownloadTimeout;
request.timeout = (int)actualTimeout;
LogMessage($"发送图片下载请求,超时时间: {actualTimeout}秒", LogType.Log);
// 使用try-catch包裹SendWebRequest,因为WebGL环境下可能抛出异常
try
{
await request.SendWebRequest();
}
catch (Exception sendEx)
{
// 捕获SendWebRequest可能抛出的异常
string errorDetails = $"发送请求时发生异常: {sendEx.GetType().Name}, 消息: {sendEx.Message}";
if (sendEx.InnerException != null)
{
errorDetails += $", 内部异常: {sendEx.InnerException.Message}";
}
LogMessage($"图片下载失败 [{imageUrl}]: {errorDetails}", LogType.Log);
LogMessage($"异常堆栈: {sendEx.StackTrace}", LogType.Log);
OnImageDownloadFailed?.Invoke(imageUrl, errorDetails);
// 清理资源
if (request != null)
{
request.Dispose();
}
return null;
}
// 检查请求状态和响应码
string statusInfo = $"HTTP状态码: {request.responseCode}, Result: {request.result}";
// 检查请求结果
if (request.result == UnityWebRequest.Result.Success)
{
LogMessage($"图片下载成功 [{imageUrl}]: {statusInfo}", LogType.Log);
// 获取下载的纹理
Texture2D texture = null;
try
{
texture = DownloadHandlerTexture.GetContent(request);
}
catch (Exception textureEx)
{
// 捕获获取纹理时可能发生的异常
string textureError = $"获取纹理时发生异常: {textureEx.GetType().Name}, 消息: {textureEx.Message}";
LogMessage($"图片下载失败 [{imageUrl}]: {textureError}", LogType.Log);
LogMessage($"异常堆栈: {textureEx.StackTrace}", LogType.Log);
OnImageDownloadFailed?.Invoke(imageUrl, textureError);
request.Dispose();
return null;
}
if (texture != null)
{
// 添加到缓存
imageCache[imageUrl] = texture;
// 触发完成事件
OnImageDownloadCompleted?.Invoke(imageUrl, texture);
LogMessage($"图片设置完成 [{imageUrl}], 尺寸: {texture.width}x{texture.height}", LogType.Log);
request.Dispose();
return texture;
}
else
{
LogMessage($"图片下载失败 [{imageUrl}]: 下载的纹理为空, {statusInfo}", LogType.Warning);
OnImageDownloadFailed?.Invoke(imageUrl, "下载的纹理为空");
request.Dispose();
return null;
}
}
else
{
// 请求失败,打印详细错误信息
string errorMsg = request.error ?? "未知错误";
string detailedError = $"图片下载失败 [{imageUrl}]: {statusInfo}, 错误信息: {errorMsg}";
// 根据HTTP状态码提供更详细的错误描述
if (request.responseCode >= 400)
{
string httpErrorMsg = request.responseCode switch
{
404 => "文件不存在 (404 Not Found)",
403 => "访问被拒绝 (403 Forbidden)",
500 => "服务器内部错误 (500 Internal Server Error)",
503 => "服务不可用 (503 Service Unavailable)",
_ => $"HTTP错误 {request.responseCode}"
};
detailedError += $", {httpErrorMsg}";
}
LogMessage(detailedError, LogType.Log);
OnImageDownloadFailed?.Invoke(imageUrl, detailedError);
request.Dispose();
return null;
}
}
catch (Exception ex)
{
// 捕获所有其他可能的异常
string errorDetails = $"下载图片时发生未预期的异常 [{imageUrl}]: {ex.GetType().Name}, 消息: {ex.Message}";
if (ex.InnerException != null)
{
errorDetails += $", 内部异常: {ex.InnerException.Message}";
}
LogMessage(errorDetails, LogType.Log);
LogMessage($"异常堆栈: {ex.StackTrace}", LogType.Log);
OnImageDownloadFailed?.Invoke(imageUrl, errorDetails);
// 确保清理资源
if (request != null)
{
request.Dispose();
}
return null;
}
}
///
/// 下载图片(回调方式)
///
/// 图片URL
/// 完成回调
/// 错误回调
/// 超时时间(秒),-1使用默认值
public void DownloadImage(string imageUrl, Action onComplete = null, Action onError = null, float timeout = -1)
{
DownloadImageAsync(imageUrl, timeout).ContinueWith(texture =>
{
if (texture != null)
{
onComplete?.Invoke(texture);
}
else
{
onError?.Invoke("图片下载失败");
}
}).Forget();
}
#endregion
}
///
/// Web请求任务类
///
[System.Serializable]
public class WebRequestTask
{
public string Url; // 请求URL
public string Method; // HTTP方法
public string Data; // 请求数据
public string ContentType; // 内容类型
public Dictionary Headers; // 请求头
public float Timeout; // 超时时间
public Action OnComplete; // 完成回调
public Action OnError; // 错误回调
public int RetryCount; // 重试次数
}
///
/// Web请求结果类
///
[System.Serializable]
public class WebRequestResult
{
public string Url; // 请求URL
public string Method; // HTTP方法
public long ResponseCode; // 响应状态码
public string ResponseText; // 响应文本
public string ResponseHeaders; // 响应头
public bool IsSuccess; // 是否成功
public string ErrorMessage; // 错误信息
}
}