using System; using System.Net; using System.Net.Http; using System.Reflection; using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; using ZonyLrcTools.Cli.Infrastructure.DependencyInject; using ZonyLrcTools.Cli.Infrastructure.Exceptions; namespace ZonyLrcTools.Cli.Infrastructure.Network { public class DefaultWarpHttpClient : IWarpHttpClient, ITransientDependency { private readonly IHttpClientFactory _httpClientFactory; public const string HttpClientNameConstant = "WarpClient"; public DefaultWarpHttpClient(IHttpClientFactory httpClientFactory) { _httpClientFactory = httpClientFactory; } public async ValueTask PostAsync(string url, object parameters = null, bool isQueryStringParam = false, Action requestOption = null) { var parametersStr = isQueryStringParam ? BuildQueryString(parameters) : BuildJsonBodyString(parameters); var requestMessage = new HttpRequestMessage(HttpMethod.Post, new Uri(url)); requestMessage.Content = new StringContent(parametersStr); requestOption?.Invoke(requestMessage); using var responseMessage = await BuildHttpClient().SendAsync(requestMessage); var responseContentString = await responseMessage.Content.ReadAsStringAsync(); return ValidateHttpResponse(responseMessage, parameters, responseContentString); } public async ValueTask PostAsync(string url, object parameters = null, bool isQueryStringParam = false, Action requestOption = null) { var responseString = await PostAsync(url, parameters, isQueryStringParam, requestOption); return ConvertHttpResponseToObject(parameters, responseString); } public async ValueTask GetAsync(string url, object parameters = null, Action requestOption = null) { var requestParamsStr = BuildQueryString(parameters); var requestMsg = new HttpRequestMessage(HttpMethod.Get, new Uri($"{url}?{requestParamsStr}")); requestOption?.Invoke(requestMsg); using var responseMessage = await BuildHttpClient().SendAsync(requestMsg); var responseContentString = await responseMessage.Content.ReadAsStringAsync(); return ValidateHttpResponse(responseMessage, parameters, responseContentString); } public async ValueTask GetAsync(string url, object parameters = null, Action requestOption = null) { var responseString = await GetAsync(url, parameters, requestOption); return ConvertHttpResponseToObject(parameters, responseString); } protected virtual HttpClient BuildHttpClient() { return _httpClientFactory.CreateClient(HttpClientNameConstant); } private string BuildQueryString(object parameters) { if (parameters == null) { return string.Empty; } var type = parameters.GetType(); if (type == typeof(string)) { return parameters as string; } var properties = type.GetProperties(); var paramBuilder = new StringBuilder(); foreach (var propertyInfo in properties) { var jsonProperty = propertyInfo.GetCustomAttribute(); var propertyName = jsonProperty != null ? jsonProperty.PropertyName : propertyInfo.Name; paramBuilder.Append($"{propertyName}={propertyInfo.GetValue(parameters)}&"); } return paramBuilder.ToString().TrimEnd('&'); } private string BuildJsonBodyString(object parameters) { if (parameters == null) return string.Empty; if (parameters is string result) return result; return JsonConvert.SerializeObject(parameters); } /// /// 校验 Http 响应 是否合法。 /// /// 执行 Http 请求之后的响应实例。 /// 执行 Http 请求时传递的参数。 /// 执行 Http 请求之后响应内容。 /// 如果响应正常,则返回具体的响应内容。 /// 如果 Http 响应不正常,则可能抛出本异常。 private string ValidateHttpResponse(HttpResponseMessage responseMessage, object requestParameters, string responseString) { return responseMessage.StatusCode switch { HttpStatusCode.OK => responseString, HttpStatusCode.ServiceUnavailable => throw new ErrorCodeException(ErrorCodes.ServiceUnavailable), _ => throw new ErrorCodeException(ErrorCodes.HttpRequestFailed, attachObj: new { requestParameters, responseString }) }; } /// /// 将 Http 响应的字符串反序列化为指定类型 的对象。 /// /// 执行 Http 请求时传递的参数。 /// 执行 Http 请求之后响应内容。 /// 需要将响应结果反序列化的目标类型。 /// 如果反序列化失败,则可能抛出本异常。 private TResponse ConvertHttpResponseToObject(object requestParameters, string responseString) { var throwException = new ErrorCodeException(ErrorCodes.HttpResponseConvertJsonFailed, attachObj: new { requestParameters, responseString }); try { var responseObj = JsonConvert.DeserializeObject(responseString); if (responseObj != null) return responseObj; throw throwException; } catch (JsonSerializationException) { throw throwException; } } } }