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();
}
}
}