Files
RhythmicWallpaper/AudioVisualizer/Tools/HttpHelper.cs

165 lines
6.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);
/// <summary>
/// 使用共享的静态HttpClient实例
/// </summary>
public HttpHelper() => _httpClient = _sharedClient;
/// <summary>
/// 使用自定义HttpClient实例
/// </summary>
public HttpHelper(HttpClient httpClient) => _httpClient = httpClient;
/// <summary>
/// 设置全局请求超时默认100秒
/// </summary>
public TimeSpan Timeout {
get => _timeout;
set {
_timeout = value;
_httpClient.Timeout = _timeout;
}
}
/// <summary>
/// 异步发送GET请求
/// </summary>
/// <param name="url">请求地址</param>
/// <param name="queryParams">查询参数</param>
/// <param name="headers">请求头</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>响应字符串</returns>
public async Task<string> GetAsync(
string url,
Dictionary<string, string>? queryParams = null,
Dictionary<string, string>? 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);
}
/// <summary>
/// 异步发送POST请求
/// </summary>
/// <param name="url">请求地址</param>
/// <param name="content">请求内容</param>
/// <param name="contentType">内容类型默认为application/json</param>
/// <param name="headers">请求头</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>响应字符串</returns>
public async Task<string> PostAsync(
string url,
object content,
string contentType = "application/json",
Dictionary<string, string>? 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<HttpResponseMessage> 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<string, string>? 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<string, string>? 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<KeyValuePair<string, string>>
?? ConvertToFormData(content)),
_ => throw new NotSupportedException($"不支持的Content-Type: {contentType}")
};
}
private static IEnumerable<KeyValuePair<string, string>> ConvertToFormData(object data) {
var formData = new List<KeyValuePair<string, string>>();
var properties = data.GetType().GetProperties();
foreach (var prop in properties) {
var value = prop.GetValue(data)?.ToString() ?? "";
formData.Add(new KeyValuePair<string, string>(prop.Name, value));
}
return formData;
}
private static async Task<string> HandleResponse(HttpResponseMessage response) {
response.EnsureSuccessStatusCode();
using var stream = await response.Content.ReadAsStreamAsync();
using var reader = new StreamReader(stream, Encoding.UTF8);
return await reader.ReadToEndAsync();
}
}
}