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请求(IMultipartFormSection)
///
public async Task> PostFormData(string url, List formData, HttpRequestConfig config = null)
{
return await SendRequestWithFormData(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;
// 添加请求头(不要手动设置 Content-Type,WWWForm 会自动设置)
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("请求失败,已重试所有次数");
}
///
/// 发送带表单的请求
///
private async Task> SendRequestWithFormData(string url, HttpMethod method, List formData, 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, formData))
{
if (method == HttpMethod.PUT)
{
request.method = "PUT";
}
request.timeout = mergedConfig.Timeout;
// 添加请求头(不要手动设置 Content-Type,WWWForm 会自动设置)
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
}
}