You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

256 lines
8.7 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System.Collections.Specialized;
using System.Net.Http.Headers;
using System.Text;
using DS.Module.Core.Extensions;
using Newtonsoft.Json;
namespace DS.Module.Core
{
/// <summary>
/// 提供对HTTP请求的低级别访问
/// </summary>
public class ApiFox : IDisposable
{
readonly HttpClient http;
/// <summary>
/// 基URI
/// </summary>
public Uri? BaseUri { get { return http.BaseAddress; } set { http.BaseAddress = value; } }
/// <summary>
/// 默认请求头
/// </summary>
public HttpRequestHeaders DefaultHeaders => http.DefaultRequestHeaders;
/// <summary>
/// 在发送网络请求之前的事件
/// </summary>
public event EventHandler<BeforeSendEventArgs>? BeforeSend;
/// <summary>
/// 在发送网络请求完成时的事件
/// </summary>
public event EventHandler<CompleteEventArgs>? Completed;
/// <summary>
/// 初始化
/// </summary>
/// <param name="pooledMinutes">连接池使用时间默认为30分钟</param>
public ApiFox(int pooledMinutes = 30)
{
var handler = new SocketsHttpHandler
{
PooledConnectionLifetime = TimeSpan.FromMinutes(pooledMinutes)
};
http = new(handler);
http.DefaultRequestHeaders.Add("User-Agent", $"X-{nameof(ApiFox)}");
http.DefaultRequestHeaders.Add("Accept", "application/json, text/plain");
}
/// <summary>
/// 发起<see cref="HttpMethod.Get"/>请求
/// </summary>
/// <typeparam name="T">请求结果的类型</typeparam>
/// <param name="url">请求Url</param>
/// <param name="keyValues">查询字符串的键值对</param>
/// <returns></returns>
/// <exception cref="ArgumentException"><paramref name="url"/>为null或空字符串</exception>
public async Task<DataResult<T>> GetAsync<T>(string url, NameValueCollection? keyValues = null)
{
string queryString = string.Empty;
if (keyValues != null && keyValues.Count > 0)
{
StringBuilder sb = new StringBuilder();
foreach (string key in keyValues.Keys)
{
var values = keyValues.GetValues(key);
if (values?.Length > 0)
{
for (int i = 0; i < values.Length; i++)
{
sb.Append($"&{key}={values[i]}");
}
}
}
if (sb.Length > 0) //移除首个&符
sb.Remove(0, 1);
queryString = sb.ToString();
}
if (!queryString.IsNullOrEmpty())
url = url.LastOrDefault() == '?' ? url + queryString : url + "?" + queryString;
var response = await SendRequestAsync(HttpMethod.Get, url);
T? data = default;
if (response != null && response.IsSuccessStatusCode)
{
string json = await response.Content.ReadAsStringAsync();
data = JsonConvert.DeserializeObject<T>(json);
return DataResult<T>.Success(data);
}
return DataResult<T>.Failed(response.ReasonPhrase);
}
/// <summary>
/// 发起<see cref="HttpMethod.Post"/>请求
/// </summary>
/// <typeparam name="T">请求结果的类型</typeparam>
/// <param name="url">请求Url</param>
/// <param name="requestParams">请求参数对象</param>
/// <returns></returns>
/// <exception cref="ArgumentException"><paramref name="url"/>为null或空字符串</exception>
public async Task<DataResult<T>> PostAsync<T>(string url, object? requestParams = null)
{
var response = await SendRequestAsync(HttpMethod.Post, url, requestParams);
T? data = default;
if (response != null && response.IsSuccessStatusCode)
{
string json = await response.Content.ReadAsStringAsync();
data = JsonConvert.DeserializeObject<T>(json);
return DataResult<T>.Success(data);
}
return DataResult<T>.Failed(response.ReasonPhrase);
}
/// <summary>
/// 发起HTTP请求
/// </summary>
/// <param name="method">请求方法</param>
/// <param name="url">请求Url</param>
/// <param name="requestParams">请求参数</param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"><paramref name="method"/>为null</exception>
/// <exception cref="ArgumentException"><paramref name="url"/>为null或空字符串</exception>
/// <exception cref="NotSupportedException"><paramref name="method"/>不等于 GET/POST/PUT/Delete 中的任何一个请求方法</exception>
public async Task<HttpResponseMessage> SendRequestAsync(HttpMethod method, string url, object? requestParams = null)
{
ArgumentNullException.ThrowIfNull(method);
ArgumentException.ThrowIfNullOrEmpty(url);
Uri? reqUri = default;
if (BaseUri == null)
{
if (!Uri.TryCreate(url, UriKind.RelativeOrAbsolute, out Uri? uri))
throw new ArgumentException("给定的URL格式无效", nameof(url));
reqUri = new Uri(url);
}
else
{
reqUri = new(BaseUri, url);
}
OnBeforeSend(new BeforeSendEventArgs
{
RequestHeaders = http.DefaultRequestHeaders,
RequestParameter = requestParams,
RequestUri = reqUri
});
HttpResponseMessage? response = null;
if (method == HttpMethod.Get)
{
response = await http.GetAsync(reqUri);
}
else if (method == HttpMethod.Post)
{
string json = JsonConvert.SerializeObject(requestParams);
var jsonRequest = new StringContent(json, Encoding.UTF8, "application/json");
response = await http.PostAsync(reqUri, jsonRequest);
}
else if (method == HttpMethod.Put)
{
string json = JsonConvert.SerializeObject(requestParams);
var jsonRequest = new StringContent(json, Encoding.UTF8, "application/json");
response = await http.PutAsync(reqUri, jsonRequest);
}
else if (method == HttpMethod.Delete)
{
response = await http.DeleteAsync(reqUri);
}
else
{
throw new NotSupportedException($"不支持的请求方法:{method.Method}");
}
OnCompleted(new CompleteEventArgs
{
RequestUri = reqUri,
Response = response
});
return response;
}
/// <summary>
/// 在发送网络请求之前调用
/// </summary>
/// <param name="e">事件参数</param>
protected virtual void OnBeforeSend(BeforeSendEventArgs e)
{
BeforeSend?.Invoke(this, e);
}
/// <summary>
/// 在发送网络请求完成时调用
/// </summary>
/// <param name="e">事件参数</param>
protected virtual void OnCompleted(CompleteEventArgs e)
{
Completed?.Invoke(this, e);
}
/// <summary>
/// 释放网络连接
/// </summary>
public void Dispose()
{
http?.Dispose();
GC.SuppressFinalize(this);
}
}
/// <summary>
/// 发送网络请求之前的事件参数
/// </summary>
public class BeforeSendEventArgs : EventArgs
{
/// <summary>
/// 远程请求的URI
/// </summary>
public Uri RequestUri { get; internal set; }
/// <summary>
/// 请求标头
/// </summary>
public HttpRequestHeaders RequestHeaders { get; internal set; }
/// <summary>
/// 请求参数
/// </summary>
public object? RequestParameter { get; set; }
}
/// <summary>
/// 发送网络请求完成的事件参数
/// </summary>
public class CompleteEventArgs : EventArgs
{
/// <summary>
/// 远程请求的URI
/// </summary>
public Uri RequestUri { get; internal set; }
/// <summary>
/// 网络响应对象
/// </summary>
public HttpResponseMessage Response { get; internal set; }
}
}