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.

250 lines
8.4 KiB
C#

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);
5 months ago
T? data = default;
if (response != null)
{
string json = await response.Content.ReadAsStringAsync();
data = JsonConvert.DeserializeObject<T>(json);
}
return DataResult<T>.Success(data);
}
/// <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);
5 months ago
T? data = default;
if (response != null)
5 months ago
{
string json = await response.Content.ReadAsStringAsync();
data = JsonConvert.DeserializeObject<T>(json);
}
return DataResult<T>.Success(data);
}
/// <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();
}
/// <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; }
}
}