using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Text; using System.Threading.Tasks; using UnityEngine; using UnityEngine.Networking; using Debug = UnityEngine.Debug; // 添加 JSON 解析支持 using Newtonsoft.Json; namespace Kill.Network { /// /// HTTP请求管理器 - 统一管理所有HTTP请求 /// public class HttpRequestManager : MonoBehaviour { public static HttpRequestManager Instance { get; private set; } /// /// 基础URL /// public string BaseUrl { get; set; } = ""; /// /// 全局请求头 /// private Dictionary _globalHeaders = new Dictionary(); /// /// 请求拦截器 /// public event Func OnRequestIntercept; /// /// 响应拦截器 /// public event Func> OnResponseIntercept; /// /// 默认请求配置 /// private HttpRequestConfig _defaultConfig = new HttpRequestConfig(); private void Awake() { if (Instance == null) { Instance = this; DontDestroyOnLoad(gameObject); } else { Destroy(gameObject); } } #region 全局配置 /// /// 设置基础URL /// public void SetBaseUrl(string baseUrl) { BaseUrl = baseUrl.TrimEnd('/'); } /// /// 添加全局请求头 /// public void AddGlobalHeader(string key, string value) { _globalHeaders[key] = value; } /// /// 移除全局请求头 /// public void RemoveGlobalHeader(string key) { if (_globalHeaders.ContainsKey(key)) { _globalHeaders.Remove(key); } } /// /// 设置默认配置 /// public void SetDefaultConfig(HttpRequestConfig config) { _defaultConfig = config; } /// /// 清除所有全局请求头 /// public void ClearGlobalHeaders() { _globalHeaders.Clear(); } #endregion #region GET请求 /// /// 发送GET请求 /// public async Task> Get(string url, Dictionary queryParams = null, HttpRequestConfig config = null) { string fullUrl = BuildUrl(url, queryParams); return await SendRequest(fullUrl, HttpMethod.GET, null, config); } /// /// 发送GET请求(返回原始字符串) /// public async Task> Get(string url, Dictionary queryParams = null, HttpRequestConfig config = null) { string fullUrl = BuildUrl(url, queryParams); return await SendRequest(fullUrl, HttpMethod.GET, null, config); } #endregion #region POST请求 /// /// 发送POST请求(JSON数据) /// public async Task> Post(string url, object data, HttpRequestConfig config = null) { string jsonData = data != null ? JsonConvert.SerializeObject(data) : ""; Debug.Log("发送数据:"+jsonData); return await SendRequest(url, HttpMethod.POST, jsonData, config, "application/json"); } /// /// 发送POST请求(表单数据) /// public async Task> PostForm(string url, WWWForm formData, HttpRequestConfig config = null) { return await SendRequestWithForm(url, HttpMethod.POST, formData, config); } /// /// 发送POST请求(原始字符串) /// public async Task> Post(string url, object data, HttpRequestConfig config = null) { return await Post(url, data, config); } #endregion #region PUT请求 /// /// 发送PUT请求(JSON数据) /// public async Task> Put(string url, object data, HttpRequestConfig config = null) { string jsonData = data != null ? JsonUtility.ToJson(data) : ""; Debug.Log(jsonData); return await SendRequest(url, HttpMethod.PUT, jsonData, config, "application/json"); } /// /// 发送PUT请求(表单数据) /// public async Task> PutForm(string url, WWWForm form, HttpRequestConfig config = null) { return await SendRequestWithForm(url, HttpMethod.PUT, form, config); } /// /// 发送PUT请求(原始字符串) /// public async Task> Put(string url, object data, HttpRequestConfig config = null) { return await Put(url, data, config); } #endregion #region DELETE请求 /// /// 发送DELETE请求 /// public async Task> Delete(string url, HttpRequestConfig config = null) { return await SendRequest(url, HttpMethod.DELETE, null, config); } /// /// 发送DELETE请求(带body) /// public async Task> Delete(string url, object data, HttpRequestConfig config = null) { string jsonData = data != null ? JsonUtility.ToJson(data) : ""; return await SendRequest(url, HttpMethod.DELETE, jsonData, config, "application/json"); } #endregion #region 文件下载 /// /// 下载文件 /// public async Task> DownloadFile(string url, string savePath, Action onProgress = null, HttpRequestConfig config = null) { var mergedConfig = MergeConfig(config); var stopwatch = Stopwatch.StartNew(); try { using (UnityWebRequest request = UnityWebRequest.Get(url)) { request.timeout = mergedConfig.Timeout; // 添加请求头 AddHeadersToRequest(request, mergedConfig.Headers); // 发送请求 var operation = request.SendWebRequest(); // 监控进度 while (!operation.isDone) { long totalBytes = 0; string contentLength = request.GetResponseHeader("Content-Length"); if (!string.IsNullOrEmpty(contentLength) && long.TryParse(contentLength, out long parsedLength)) { totalBytes = parsedLength; } onProgress?.Invoke(new DownloadProgress { DownloadedBytes = (long)(request.downloadedBytes), TotalBytes = totalBytes }); await Task.Yield(); } stopwatch.Stop(); if (request.result == UnityWebRequest.Result.Success) { // 确保目录存在 string directory = Path.GetDirectoryName(savePath); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } // 保存文件 File.WriteAllBytes(savePath, request.downloadHandler.data); return new HttpResponse { IsSuccess = true, StatusCode = request.responseCode, Data = savePath, ElapsedMilliseconds = stopwatch.ElapsedMilliseconds }; } else { return HttpResponse.Failure(request.error, request.responseCode); } } } catch (Exception ex) { stopwatch.Stop(); return HttpResponse.Failure($"下载异常: {ex.Message}"); } } #endregion #region 核心请求方法 /// /// 发送HTTP请求(核心方法) /// private async Task> SendRequest(string url, HttpMethod method, string data, HttpRequestConfig config, string contentType = null) { var mergedConfig = MergeConfig(config); string fullUrl = GetFullUrl(url); // 检查网络状态 if (NetworkStateManager.Instance != null && !NetworkStateManager.Instance.IsConnected) { return HttpResponse.Failure("网络未连接"); } int retryCount = 0; while (retryCount <= mergedConfig.MaxRetries) { var stopwatch = Stopwatch.StartNew(); try { using (UnityWebRequest request = CreateWebRequest(fullUrl, method, data, contentType)) { request.timeout = mergedConfig.Timeout; // 添加请求头 AddHeadersToRequest(request, mergedConfig.Headers); // 发送请求 var operation = request.SendWebRequest(); while (!operation.isDone) { await Task.Yield(); } stopwatch.Stop(); Debug.Log(request.uri.ToString()); // 处理响应 if (request.result == UnityWebRequest.Result.Success) { string responseText = request.downloadHandler.text; Debug.Log("response:"+responseText); // 响应拦截器 if (OnResponseIntercept != null) { responseText = await OnResponseIntercept.Invoke(responseText); } T resultData = default; if (!string.IsNullOrEmpty(responseText) && typeof(T) != typeof(string)) { try { // 使用 Newtonsoft.Json 解析,支持嵌套对象 resultData = JsonConvert.DeserializeObject(responseText); } catch (Exception ex) { Debug.LogWarning($"Newtonsoft JSON解析失败: {ex.Message},尝试使用 Unity 的 JsonUtility"); try { // 降级使用 Unity 的 JsonUtility resultData = JsonUtility.FromJson(responseText); } catch (Exception ex2) { Debug.LogWarning($"JsonUtility 解析也失败: {ex2.Message}"); resultData = (T)(object)responseText; } } } else if (typeof(T) == typeof(string)) { resultData = (T)(object)responseText; } return new HttpResponse { IsSuccess = true, StatusCode = request.responseCode, Data = resultData, RawResponse = responseText, Headers = GetResponseHeaders(request), ElapsedMilliseconds = stopwatch.ElapsedMilliseconds }; } else { // 请求失败,检查是否需要重试 if (retryCount < mergedConfig.MaxRetries && ShouldRetry(request.responseCode)) { retryCount++; int delay = mergedConfig.UseExponentialBackoff ? mergedConfig.RetryDelayMs * (int)Math.Pow(2, retryCount - 1) : mergedConfig.RetryDelayMs; Debug.Log($"请求失败,{delay}ms后重试({retryCount}/{mergedConfig.MaxRetries}): {fullUrl}"); await Task.Delay(delay); continue; } return HttpResponse.Failure(request.error, request.responseCode); } } } catch (Exception ex) { stopwatch.Stop(); if (retryCount < mergedConfig.MaxRetries) { retryCount++; int delay = mergedConfig.UseExponentialBackoff ? mergedConfig.RetryDelayMs * (int)Math.Pow(2, retryCount - 1) : mergedConfig.RetryDelayMs; Debug.Log($"请求异常,{delay}ms后重试({retryCount}/{mergedConfig.MaxRetries}): {ex.Message}"); await Task.Delay(delay); continue; } return HttpResponse.Failure($"请求异常: {ex.Message}"); } } return HttpResponse.Failure("请求失败,已重试所有次数"); } /// /// 发送带表单的请求 /// private async Task> SendRequestWithForm(string url, HttpMethod method, WWWForm form, HttpRequestConfig config) { var mergedConfig = MergeConfig(config); string fullUrl = GetFullUrl(url); int retryCount = 0; while (retryCount <= mergedConfig.MaxRetries) { var stopwatch = Stopwatch.StartNew(); try { using (UnityWebRequest request = UnityWebRequest.Post(fullUrl, form)) { if (method == HttpMethod.PUT) { request.method = "PUT"; } request.timeout = mergedConfig.Timeout; // 添加请求头 AddHeadersToRequest(request, mergedConfig.Headers); var operation = request.SendWebRequest(); while (!operation.isDone) { await Task.Yield(); } stopwatch.Stop(); if (request.result == UnityWebRequest.Result.Success) { string responseText = request.downloadHandler.text; T resultData = default; if (!string.IsNullOrEmpty(responseText) && typeof(T) != typeof(string)) { try { resultData = JsonUtility.FromJson(responseText); } catch { resultData = (T)(object)responseText; } } else if (typeof(T) == typeof(string)) { resultData = (T)(object)responseText; } return new HttpResponse { IsSuccess = true, StatusCode = request.responseCode, Data = resultData, RawResponse = responseText, ElapsedMilliseconds = stopwatch.ElapsedMilliseconds }; } else { if (retryCount < mergedConfig.MaxRetries && ShouldRetry(request.responseCode)) { retryCount++; int delay = mergedConfig.UseExponentialBackoff ? mergedConfig.RetryDelayMs * (int)Math.Pow(2, retryCount - 1) : mergedConfig.RetryDelayMs; await Task.Delay(delay); continue; } return HttpResponse.Failure(request.error, request.responseCode); } } } catch (Exception ex) { stopwatch.Stop(); if (retryCount < mergedConfig.MaxRetries) { retryCount++; int delay = mergedConfig.UseExponentialBackoff ? mergedConfig.RetryDelayMs * (int)Math.Pow(2, retryCount - 1) : mergedConfig.RetryDelayMs; await Task.Delay(delay); continue; } return HttpResponse.Failure($"请求异常: {ex.Message}"); } } return HttpResponse.Failure("请求失败,已重试所有次数"); } #endregion #region 辅助方法 /// /// 创建WebRequest /// private UnityWebRequest CreateWebRequest(string url, HttpMethod method, string data, string contentType) { UnityWebRequest request; switch (method) { case HttpMethod.GET: request = UnityWebRequest.Get(url); break; case HttpMethod.POST: request = UnityWebRequest.Post(url, data, contentType ?? "application/json"); break; case HttpMethod.PUT: byte[] bodyRaw = Encoding.UTF8.GetBytes(data ?? ""); request = UnityWebRequest.Put(url, bodyRaw); request.SetRequestHeader("Content-Type", contentType ?? "application/json"); break; case HttpMethod.DELETE: request = UnityWebRequest.Delete(url); if (!string.IsNullOrEmpty(data)) { byte[] deleteBody = Encoding.UTF8.GetBytes(data); request.uploadHandler = new UploadHandlerRaw(deleteBody); request.SetRequestHeader("Content-Type", contentType ?? "application/json"); } break; default: request = UnityWebRequest.Get(url); break; } // 设置下载处理器 if (request.downloadHandler == null) { request.downloadHandler = new DownloadHandlerBuffer(); } return request; } /// /// 添加请求头 /// private void AddHeadersToRequest(UnityWebRequest request, Dictionary customHeaders) { // 添加全局请求头 foreach (var header in _globalHeaders) { request.SetRequestHeader(header.Key, header.Value); } // 添加自定义请求头 if (customHeaders != null) { foreach (var header in customHeaders) { request.SetRequestHeader(header.Key, header.Value); } } } /// /// 获取响应头 /// private Dictionary GetResponseHeaders(UnityWebRequest request) { var headers = new Dictionary(); if (request.GetResponseHeaders() != null) { foreach (var header in request.GetResponseHeaders()) { headers[header.Key] = header.Value; } } return headers; } /// /// 合并配置 /// private HttpRequestConfig MergeConfig(HttpRequestConfig customConfig) { if (customConfig == null) return _defaultConfig; return new HttpRequestConfig { Timeout = customConfig.Timeout > 0 ? customConfig.Timeout : _defaultConfig.Timeout, MaxRetries = customConfig.MaxRetries >= 0 ? customConfig.MaxRetries : _defaultConfig.MaxRetries, RetryDelayMs = customConfig.RetryDelayMs > 0 ? customConfig.RetryDelayMs : _defaultConfig.RetryDelayMs, UseExponentialBackoff = customConfig.UseExponentialBackoff, Headers = customConfig.Headers ?? _defaultConfig.Headers, Priority = customConfig.Priority, EnableCache = customConfig.EnableCache, CacheDuration = customConfig.CacheDuration > 0 ? customConfig.CacheDuration : _defaultConfig.CacheDuration }; } /// /// 构建完整URL /// private string GetFullUrl(string url) { if (string.IsNullOrEmpty(BaseUrl) || url.StartsWith("http://") || url.StartsWith("https://")) { return url; } return $"{BaseUrl}/{url.TrimStart('/')}"; } /// /// 构建带查询参数的URL /// private string BuildUrl(string url, Dictionary queryParams) { string fullUrl = GetFullUrl(url); if (queryParams == null || queryParams.Count == 0) { return fullUrl; } StringBuilder sb = new StringBuilder(fullUrl); sb.Append(fullUrl.Contains("?") ? "&" : "?"); bool first = true; foreach (var param in queryParams) { if (!first) sb.Append("&"); sb.Append($"{UnityWebRequest.EscapeURL(param.Key)}={UnityWebRequest.EscapeURL(param.Value)}"); first = false; } return sb.ToString(); } /// /// 判断是否应该重试 /// private bool ShouldRetry(long statusCode) { // 5xx 服务器错误或 0(网络错误) 时重试 return statusCode >= 500 || statusCode == 0 || statusCode == 408; } #endregion } }