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; // 错误信息 } }