using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using System.Web; namespace AudioWallpaper.Tools { public class HttpHelper { // 使用静态HttpClient实例(推荐) private static readonly HttpClient _sharedClient = new HttpClient( new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate } ); private readonly HttpClient _httpClient; private TimeSpan _timeout = TimeSpan.FromSeconds(100); /// /// 使用共享的静态HttpClient实例 /// public HttpHelper() => _httpClient = _sharedClient; /// /// 使用自定义HttpClient实例 /// public HttpHelper(HttpClient httpClient) => _httpClient = httpClient; /// /// 设置全局请求超时(默认100秒) /// public TimeSpan Timeout { get => _timeout; set { _timeout = value; _httpClient.Timeout = _timeout; } } /// /// 异步发送GET请求 /// /// 请求地址 /// 查询参数 /// 请求头 /// 取消令牌 /// 响应字符串 public async Task GetAsync( string url, Dictionary? queryParams = null, Dictionary? headers = null, CancellationToken cancellationToken = default) { var requestUrl = BuildUrlWithQuery(url, queryParams); using var request = new HttpRequestMessage(HttpMethod.Get, requestUrl); AddHeaders(request, headers); using var response = await SendWithTimeoutAsync(request, cancellationToken); return await HandleResponse(response); } /// /// 异步发送POST请求 /// /// 请求地址 /// 请求内容 /// 内容类型(默认为application/json) /// 请求头 /// 取消令牌 /// 响应字符串 public async Task PostAsync( string url, object content, string contentType = "application/json", Dictionary? headers = null, CancellationToken cancellationToken = default) { using var request = new HttpRequestMessage(HttpMethod.Post, url); AddHeaders(request, headers); request.Content = CreateHttpContent(content, contentType); using var response = await SendWithTimeoutAsync(request, cancellationToken); return await HandleResponse(response); } private async Task SendWithTimeoutAsync( HttpRequestMessage request, CancellationToken cancellationToken) { try { // 使用CancellationTokenSource实现超时控制 using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); cts.CancelAfter(_timeout); return await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cts.Token); } catch (TaskCanceledException) when (!cancellationToken.IsCancellationRequested) { throw new TimeoutException($"请求超时 ({_timeout.TotalSeconds}秒)"); } } private static string BuildUrlWithQuery(string url, Dictionary? queryParams) { if (queryParams == null || queryParams.Count == 0) return url; var uriBuilder = new UriBuilder(url); var query = HttpUtility.ParseQueryString(uriBuilder.Query); foreach (var param in queryParams) { query[param.Key] = param.Value; } uriBuilder.Query = query.ToString(); return uriBuilder.ToString(); } private static void AddHeaders(HttpRequestMessage request, Dictionary? headers) { if (headers == null) return; foreach (var header in headers) { if (header.Key.Equals("Content-Type", StringComparison.OrdinalIgnoreCase)) { if (request.Content != null) { request.Content.Headers.ContentType = new MediaTypeHeaderValue(header.Value); } } else { request.Headers.TryAddWithoutValidation(header.Key, header.Value); } } } private static HttpContent CreateHttpContent(object content, string contentType) { return contentType.ToLower() switch { "application/json" => new StringContent( JsonSerializer.Serialize(content), Encoding.UTF8, "application/json"), "application/x-www-form-urlencoded" => new FormUrlEncodedContent( content as IEnumerable> ?? ConvertToFormData(content)), _ => throw new NotSupportedException($"不支持的Content-Type: {contentType}") }; } private static IEnumerable> ConvertToFormData(object data) { var formData = new List>(); var properties = data.GetType().GetProperties(); foreach (var prop in properties) { var value = prop.GetValue(data)?.ToString() ?? ""; formData.Add(new KeyValuePair(prop.Name, value)); } return formData; } private static async Task HandleResponse(HttpResponseMessage response) { response.EnsureSuccessStatusCode(); using var stream = await response.Content.ReadAsStreamAsync(); using var reader = new StreamReader(stream, Encoding.UTF8); return await reader.ReadToEndAsync(); } } }