|
|
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 result = await SendRequestAsync(HttpMethod.Get, url);
|
|
|
if (!result.Succeeded)
|
|
|
return DataResult<T>.Failed(result.Message, result.MultiCode);
|
|
|
|
|
|
var response = result.Data;
|
|
|
T? data = default;
|
|
|
if (response != null)
|
|
|
{
|
|
|
string json = await response.Content.ReadAsStringAsync();
|
|
|
data = JsonConvert.DeserializeObject<T>(json);
|
|
|
}
|
|
|
|
|
|
return new DataResult<T>(result.Code, result.Message, data, result.MultiCode);
|
|
|
}
|
|
|
|
|
|
/// <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 result = await SendRequestAsync(HttpMethod.Post, url, requestParams);
|
|
|
if (!result.Succeeded)
|
|
|
return DataResult<T>.Failed(result.Message, result.MultiCode);
|
|
|
|
|
|
var response = result.Data;
|
|
|
T? data = default;
|
|
|
if (response != null)
|
|
|
{
|
|
|
string json = await response.Content.ReadAsStringAsync();
|
|
|
data = JsonConvert.DeserializeObject<T>(json);
|
|
|
}
|
|
|
|
|
|
return new DataResult<T>(result.Code, result.Message, data, result.MultiCode);
|
|
|
}
|
|
|
|
|
|
/// <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<DataResult<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
|
|
|
});
|
|
|
|
|
|
try
|
|
|
{
|
|
|
HttpResponseMessage? response = null;
|
|
|
if (method == HttpMethod.Get)
|
|
|
{
|
|
|
response = await http.GetAsync(reqUri);
|
|
|
}
|
|
|
else if (method == HttpMethod.Post)
|
|
|
{
|
|
|
var jsonRequest = new StringContent(JsonConvert.SerializeObject(requestParams), Encoding.UTF8, "application/json");
|
|
|
response = await http.PostAsync(reqUri, jsonRequest);
|
|
|
}
|
|
|
else if (method == HttpMethod.Put)
|
|
|
{
|
|
|
var jsonRequest = new StringContent(JsonConvert.SerializeObject(requestParams), 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
|
|
|
});
|
|
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
|
return DataResult<HttpResponseMessage>.FailedData(response, string.Format(MultiLanguageConst.HttpRequestFailed, $"{response.StatusCode}"));
|
|
|
|
|
|
return DataResult<HttpResponseMessage>.Success(response);
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
return DataResult<HttpResponseMessage>.Failed(ex.Message);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <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; }
|
|
|
}
|
|
|
}
|