1095 lines
39 KiB
C#
1095 lines
39 KiB
C#
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
|
||
{
|
||
/// <summary>
|
||
/// WebRequest管理器 - 简化版HTTP请求管理器
|
||
/// 支持GET、POST请求,自动处理token认证
|
||
/// </summary>
|
||
public class WebRequestManager : MonoBehaviour
|
||
{
|
||
#region 单例模式
|
||
|
||
private static WebRequestManager _instance;
|
||
|
||
public static WebRequestManager Instance
|
||
{
|
||
get
|
||
{
|
||
if (_instance == null)
|
||
{
|
||
// 查找场景中是否已存在实例
|
||
_instance = FindObjectOfType<WebRequestManager>();
|
||
|
||
if (_instance == null)
|
||
{
|
||
// 创建新的GameObject并添加组件
|
||
GameObject go = new GameObject("WebRequestManager");
|
||
_instance = go.AddComponent<WebRequestManager>();
|
||
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<WebRequestTask> requestQueue = new Queue<WebRequestTask>(); // 请求队列
|
||
private List<WebRequestTask> activeRequests = new List<WebRequestTask>(); // 活跃请求列表
|
||
private Dictionary<string, string> defaultHeaders = new Dictionary<string, string>(); // 默认请求头
|
||
|
||
// 图片下载相关字段
|
||
private Dictionary<string, Texture2D> imageCache = new Dictionary<string, Texture2D>(); // 简单图片缓存
|
||
|
||
#endregion
|
||
|
||
#region 事件定义
|
||
|
||
/// <summary>
|
||
/// 请求完成事件
|
||
/// </summary>
|
||
public event Action<WebRequestResult> OnRequestCompleted;
|
||
|
||
/// <summary>
|
||
/// 请求失败事件
|
||
/// </summary>
|
||
public event Action<WebRequestResult> OnRequestFailed;
|
||
|
||
/// <summary>
|
||
/// 网络错误事件
|
||
/// </summary>
|
||
public event Action<string> OnNetworkError;
|
||
|
||
/// <summary>
|
||
/// 图片下载完成事件
|
||
/// </summary>
|
||
public event Action<string, Texture2D> OnImageDownloadCompleted;
|
||
|
||
/// <summary>
|
||
/// 图片下载失败事件
|
||
/// </summary>
|
||
public event Action<string, string> 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 初始化方法
|
||
|
||
/// <summary>
|
||
/// 初始化管理器
|
||
/// </summary>
|
||
private void InitializeManager()
|
||
{
|
||
// 设置默认请求头(不设置Content-Type,让每个请求自己决定)
|
||
defaultHeaders["Accept"] = "application/json";
|
||
|
||
LogMessage("WebRequestManager初始化完成", LogType.Log);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置默认请求头
|
||
/// </summary>
|
||
/// <param name="key">请求头键</param>
|
||
/// <param name="value">请求头值</param>
|
||
public void SetDefaultHeader(string key, string value)
|
||
{
|
||
if (string.IsNullOrEmpty(key))
|
||
{
|
||
LogMessage("设置默认请求头失败:键不能为空", LogType.Warning);
|
||
return;
|
||
}
|
||
|
||
defaultHeaders[key] = value;
|
||
LogMessage($"设置默认请求头: {key} = {value}", LogType.Log);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清除默认请求头
|
||
/// </summary>
|
||
public void ClearDefaultHeaders()
|
||
{
|
||
defaultHeaders.Clear();
|
||
LogMessage("已清除所有默认请求头", LogType.Log);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 日志方法
|
||
|
||
/// <summary>
|
||
/// 记录日志消息
|
||
/// </summary>
|
||
/// <param name="message">日志消息</param>
|
||
/// <param name="logType">日志类型</param>
|
||
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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 记录详细日志(仅在启用详细日志时输出)
|
||
/// </summary>
|
||
/// <param name="message">详细日志消息</param>
|
||
private void LogVerbose(string message)
|
||
{
|
||
if (enableVerboseLogging)
|
||
{
|
||
LogMessage($"[详细] {message}", LogType.Log);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 辅助方法
|
||
|
||
/// <summary>
|
||
/// 转换参数值为字符串
|
||
/// </summary>
|
||
/// <param name="value">参数值</param>
|
||
/// <returns>字符串值</returns>
|
||
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 公共接口方法
|
||
|
||
/// <summary>
|
||
/// 发送GET请求(协程方式)
|
||
/// </summary>
|
||
/// <param name="url">请求URL</param>
|
||
/// <param name="onComplete">完成回调</param>
|
||
/// <param name="onError">错误回调</param>
|
||
/// <param name="enableSign">是否启用sign签名</param>
|
||
/// <param name="signParams">签名参数(当enableSign为true时使用)</param>
|
||
public void GetRequest(string url, Action<WebRequestResult> onComplete = null, Action<string> onError = null,
|
||
bool enableSign = false, Dictionary<string, string> signParams = null)
|
||
{
|
||
if (string.IsNullOrEmpty(url))
|
||
{
|
||
LogMessage("GET请求失败:URL不能为空", LogType.Error);
|
||
onError?.Invoke("URL不能为空");
|
||
return;
|
||
}
|
||
|
||
// 获取token并添加到请求头
|
||
var requestHeaders = new Dictionary<string, string>();
|
||
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<string, string>(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<string>();
|
||
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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发送POST请求(自动处理token,使用JSON格式)
|
||
/// </summary>
|
||
/// <param name="url">请求URL</param>
|
||
/// <param name="onComplete">完成回调</param>
|
||
/// <param name="onError">错误回调</param>
|
||
/// <param name="enableSign">是否启用sign签名</param>
|
||
/// <param name="signParams">签名参数(当enableSign为true时使用)</param>
|
||
public void PostRequest(string url, Action<WebRequestResult> onComplete = null, Action<string> onError = null,
|
||
bool enableSign = true, Dictionary<string, string> signParams = null)
|
||
{
|
||
if (string.IsNullOrEmpty(url))
|
||
{
|
||
LogMessage("POST请求失败:URL不能为空", LogType.Error);
|
||
onError?.Invoke("URL不能为空");
|
||
return;
|
||
}
|
||
|
||
// 获取token并添加到请求头
|
||
var requestHeaders = new Dictionary<string, string>();
|
||
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<string, string> fullParams = null;
|
||
// 创建完整的参数字典
|
||
if (signParams != null)
|
||
fullParams = new Dictionary<string, string>(signParams);
|
||
else
|
||
{
|
||
fullParams = new Dictionary<string, string>();
|
||
}
|
||
|
||
// 添加固定的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);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发送POST请求(JSON数据)
|
||
/// </summary>
|
||
/// <param name="url">请求URL</param>
|
||
/// <param name="jsonData">JSON数据</param>
|
||
/// <param name="headers">自定义请求头</param>
|
||
/// <param name="timeout">超时时间</param>
|
||
/// <param name="onComplete">完成回调</param>
|
||
/// <param name="onError">错误回调</param>
|
||
public void PostJsonRequest(string url, string jsonData, Dictionary<string, string> headers = null,
|
||
float timeout = -1, Action<WebRequestResult> onComplete = null, Action<string> onError = null)
|
||
{
|
||
// 直接使用UnityWebRequest发送,因为这是旧的方法
|
||
StartCoroutine(PostJsonCoroutine(url, jsonData, headers, timeout, onComplete, onError));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发送POST请求(表单数据)
|
||
/// </summary>
|
||
/// <param name="url">请求URL</param>
|
||
/// <param name="formData">表单数据</param>
|
||
/// <param name="headers">自定义请求头</param>
|
||
/// <param name="timeout">超时时间</param>
|
||
/// <param name="onComplete">完成回调</param>
|
||
/// <param name="onError">错误回调</param>
|
||
public void PostFormRequest(string url, Dictionary<string, string> formData, Dictionary<string, string> headers = null,
|
||
float timeout = -1, Action<WebRequestResult> onComplete = null, Action<string> onError = null)
|
||
{
|
||
string formString = "";
|
||
if (formData != null && formData.Count > 0)
|
||
{
|
||
var formList = new List<string>();
|
||
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 协程方法(用于旧方法)
|
||
|
||
/// <summary>
|
||
/// POST JSON请求协程
|
||
/// </summary>
|
||
private IEnumerator PostJsonCoroutine(string url, string jsonData, Dictionary<string, string> headers,
|
||
float timeout, Action<WebRequestResult> onComplete, Action<string> 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();
|
||
}
|
||
|
||
/// <summary>
|
||
/// POST表单请求协程
|
||
/// </summary>
|
||
private IEnumerator PostFormCoroutine(string url, string formData, Dictionary<string, string> headers,
|
||
float timeout, Action<WebRequestResult> onComplete, Action<string> 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)
|
||
|
||
/// <summary>
|
||
/// 发送GET请求(异步方式,返回UniTask)
|
||
/// </summary>
|
||
/// <param name="url">请求URL</param>
|
||
/// <param name="enableSign">是否启用sign签名</param>
|
||
/// <param name="signParams">签名参数(当enableSign为true时使用)</param>
|
||
/// <returns>UniTask<WebRequestResult></returns>
|
||
public async UniTask<WebRequestResult> GetRequestAsync(string url, bool enableSign = false, Dictionary<string, string> signParams = null)
|
||
{
|
||
var completionSource = new UniTaskCompletionSource<WebRequestResult>();
|
||
|
||
GetRequest(url,
|
||
onComplete: (result) => { completionSource.TrySetResult(result); },
|
||
onError: (error) => { completionSource.TrySetException(new System.Exception(error)); },
|
||
enableSign: enableSign,
|
||
signParams: signParams
|
||
);
|
||
|
||
return await completionSource.Task;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发送POST请求(异步方式,返回UniTask)
|
||
/// </summary>
|
||
/// <param name="url">请求URL</param>
|
||
/// <param name="enableSign">是否启用sign签名</param>
|
||
/// <param name="signParams">签名参数(当enableSign为true时使用)</param>
|
||
/// <returns>UniTask<WebRequestResult></returns>
|
||
public async UniTask<WebRequestResult> PostRequestAsync(string url, Dictionary<string, string> signParams = null)
|
||
{
|
||
var completionSource = new UniTaskCompletionSource<WebRequestResult>();
|
||
|
||
PostRequest(url,
|
||
onComplete: (result) => { completionSource.TrySetResult(result); },
|
||
onError: (error) => { completionSource.TrySetException(new System.Exception(error)); },
|
||
|
||
signParams: signParams
|
||
);
|
||
|
||
return await completionSource.Task;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发送POST请求(JSON数据,异步方式,返回UniTask)
|
||
/// </summary>
|
||
/// <param name="url">请求URL</param>
|
||
/// <param name="jsonData">JSON数据</param>
|
||
/// <param name="headers">自定义请求头</param>
|
||
/// <param name="timeout">超时时间</param>
|
||
/// <returns>UniTask<WebRequestResult></returns>
|
||
public async UniTask<WebRequestResult> PostJsonRequestAsync(string url, string jsonData, Dictionary<string, string> headers = null, float timeout = -1)
|
||
{
|
||
var completionSource = new UniTaskCompletionSource<WebRequestResult>();
|
||
|
||
PostJsonRequest(url, jsonData, headers, timeout,
|
||
onComplete: (result) => { completionSource.TrySetResult(result); },
|
||
onError: (error) => { completionSource.TrySetException(new System.Exception(error)); }
|
||
);
|
||
|
||
return await completionSource.Task;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发送POST请求(表单数据,异步方式,返回UniTask)
|
||
/// </summary>
|
||
/// <param name="url">请求URL</param>
|
||
/// <param name="formData">表单数据</param>
|
||
/// <param name="headers">自定义请求头</param>
|
||
/// <param name="timeout">超时时间</param>
|
||
/// <returns>UniTask<WebRequestResult></returns>
|
||
public async UniTask<WebRequestResult> PostFormRequestAsync(string url, Dictionary<string, string> formData, Dictionary<string, string> headers = null, float timeout = -1)
|
||
{
|
||
var completionSource = new UniTaskCompletionSource<WebRequestResult>();
|
||
|
||
PostFormRequest(url, formData, headers, timeout,
|
||
onComplete: (result) => { completionSource.TrySetResult(result); },
|
||
onError: (error) => { completionSource.TrySetException(new System.Exception(error)); }
|
||
);
|
||
|
||
return await completionSource.Task;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 请求队列管理
|
||
|
||
/// <summary>
|
||
/// 将请求加入队列
|
||
/// </summary>
|
||
/// <param name="task">请求任务</param>
|
||
private void EnqueueRequest(WebRequestTask task)
|
||
{
|
||
requestQueue.Enqueue(task);
|
||
LogVerbose($"请求已加入队列: {task.Method} {task.Url}");
|
||
|
||
// 尝试处理队列中的请求
|
||
ProcessQueue();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理请求队列
|
||
/// </summary>
|
||
private void ProcessQueue()
|
||
{
|
||
// 如果当前活跃请求数未达到最大限制,且队列中有待处理请求
|
||
while (activeRequests.Count < maxConcurrentRequests && requestQueue.Count > 0)
|
||
{
|
||
var task = requestQueue.Dequeue();
|
||
activeRequests.Add(task);
|
||
StartCoroutine(ExecuteRequest(task));
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行请求
|
||
/// </summary>
|
||
/// <param name="task">请求任务</param>
|
||
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();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建UnityWebRequest对象
|
||
/// </summary>
|
||
/// <param name="task">请求任务</param>
|
||
/// <returns>UnityWebRequest对象</returns>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理请求结果
|
||
/// </summary>
|
||
/// <param name="task">请求任务</param>
|
||
/// <param name="request">UnityWebRequest对象</param>
|
||
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);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理请求错误
|
||
/// </summary>
|
||
/// <param name="task">请求任务</param>
|
||
/// <param name="error">错误信息</param>
|
||
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);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 延迟重试
|
||
/// </summary>
|
||
/// <param name="task">请求任务</param>
|
||
private IEnumerator DelayedRetry(WebRequestTask task)
|
||
{
|
||
yield return new WaitForSeconds(retryDelay);
|
||
EnqueueRequest(task);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 公共工具方法
|
||
|
||
/// <summary>
|
||
/// 取消所有请求
|
||
/// </summary>
|
||
public void CancelAllRequests()
|
||
{
|
||
LogMessage("取消所有请求", LogType.Warning);
|
||
|
||
// 清空队列
|
||
requestQueue.Clear();
|
||
|
||
// 停止所有活跃请求
|
||
foreach (var task in activeRequests)
|
||
{
|
||
task.OnError?.Invoke("请求被取消");
|
||
}
|
||
|
||
activeRequests.Clear();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前活跃请求数
|
||
/// </summary>
|
||
/// <returns>活跃请求数量</returns>
|
||
public int GetActiveRequestCount()
|
||
{
|
||
return activeRequests.Count;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取队列中等待的请求数
|
||
/// </summary>
|
||
/// <returns>等待请求数量</returns>
|
||
public int GetQueuedRequestCount()
|
||
{
|
||
return requestQueue.Count;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 图片下载功能
|
||
|
||
/// <summary>
|
||
/// 异步下载图片(UniTask方式)
|
||
/// </summary>
|
||
/// <param name="imageUrl">图片URL</param>
|
||
/// <param name="timeout">超时时间(秒),-1使用默认值</param>
|
||
/// <returns>下载的纹理,失败返回null</returns>
|
||
public async UniTask<Texture2D> DownloadImageAsync(string imageUrl, float timeout = -1)
|
||
{
|
||
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下载图片
|
||
using (UnityWebRequest request = UnityWebRequestTexture.GetTexture(imageUrl))
|
||
{
|
||
// 设置超时时间
|
||
float actualTimeout = timeout > 0 ? timeout : imageDownloadTimeout;
|
||
request.timeout = (int)actualTimeout;
|
||
|
||
LogMessage("发送图片下载请求", LogType.Log);
|
||
await request.SendWebRequest();
|
||
|
||
// 检查请求结果
|
||
if (request.result == UnityWebRequest.Result.Success)
|
||
{
|
||
LogMessage("图片下载成功", LogType.Log);
|
||
|
||
// 获取下载的纹理
|
||
Texture2D texture = DownloadHandlerTexture.GetContent(request);
|
||
|
||
if (texture != null)
|
||
{
|
||
// 添加到缓存
|
||
imageCache[imageUrl] = texture;
|
||
|
||
// 触发完成事件
|
||
OnImageDownloadCompleted?.Invoke(imageUrl, texture);
|
||
|
||
LogMessage($"图片设置完成,尺寸: {texture.width}x{texture.height}", LogType.Log);
|
||
return texture;
|
||
}
|
||
else
|
||
{
|
||
LogMessage("下载的纹理为空", LogType.Warning);
|
||
OnImageDownloadFailed?.Invoke(imageUrl, "下载的纹理为空");
|
||
return null;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
LogMessage($"图片下载失败: {request.error}", LogType.Error);
|
||
OnImageDownloadFailed?.Invoke(imageUrl, request.error);
|
||
return null;
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogMessage($"下载图片时发生异常: {ex.Message}", LogType.Error);
|
||
OnImageDownloadFailed?.Invoke(imageUrl, ex.Message);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 下载图片(回调方式)
|
||
/// </summary>
|
||
/// <param name="imageUrl">图片URL</param>
|
||
/// <param name="onComplete">完成回调</param>
|
||
/// <param name="onError">错误回调</param>
|
||
/// <param name="timeout">超时时间(秒),-1使用默认值</param>
|
||
public void DownloadImage(string imageUrl, Action<Texture2D> onComplete = null, Action<string> onError = null, float timeout = -1)
|
||
{
|
||
DownloadImageAsync(imageUrl, timeout).ContinueWith(texture =>
|
||
{
|
||
if (texture != null)
|
||
{
|
||
onComplete?.Invoke(texture);
|
||
}
|
||
else
|
||
{
|
||
onError?.Invoke("图片下载失败");
|
||
}
|
||
}).Forget();
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
|
||
/// <summary>
|
||
/// Web请求任务类
|
||
/// </summary>
|
||
[System.Serializable]
|
||
public class WebRequestTask
|
||
{
|
||
public string Url; // 请求URL
|
||
public string Method; // HTTP方法
|
||
public string Data; // 请求数据
|
||
public string ContentType; // 内容类型
|
||
public Dictionary<string, string> Headers; // 请求头
|
||
public float Timeout; // 超时时间
|
||
public Action<WebRequestResult> OnComplete; // 完成回调
|
||
public Action<string> OnError; // 错误回调
|
||
public int RetryCount; // 重试次数
|
||
}
|
||
|
||
/// <summary>
|
||
/// Web请求结果类
|
||
/// </summary>
|
||
[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; // 错误信息
|
||
}
|
||
} |