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.

248 lines
8.6 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
{
/// <summary>
/// 基URI
/// </summary>
public Uri? BaseUri { get; set; }
/// <summary>
/// 默认请求头
/// </summary>
public IDictionary<string, string> DefaultHeaders { get; private set; }
/// <summary>
/// 在发送网络请求之前的事件
/// </summary>
public event EventHandler<BeforeSendEventArgs>? BeforeSend;
/// <summary>
/// 在发送网络请求完成时的事件
/// </summary>
public event EventHandler<CompleteEventArgs>? Completed;
/// <summary>
/// 初始化
/// </summary>
public ApiFox()
{
DefaultHeaders = new Dictionary<string, string>();
}
/// <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;
string json = await response.Content.ReadAsStringAsync();
return DataResult<T>.Success(JsonConvert.DeserializeObject<T>(json));
}
/// <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;
string json = await response.Content.ReadAsStringAsync();
return DataResult<T>.Success(JsonConvert.DeserializeObject<T>(json));
}
/// <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>
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);
}
HttpClient http = new();
http.DefaultRequestHeaders.Add("User-Agent", $"X-{nameof(ApiFox)}");
if (DefaultHeaders.Count > 0)
foreach (var header in DefaultHeaders)
http.DefaultRequestHeaders.Add(header.Key, header.Value);
if (!http.DefaultRequestHeaders.Contains("Accept"))
http.DefaultRequestHeaders.Add("Accept", "application/json, text/plain");
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);
}
finally
{
http?.Dispose();
}
}
/// <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 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; }
}
}