You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
234 lines
11 KiB
234 lines
11 KiB
using Microsoft.Extensions.Logging;
|
|
using Newtonsoft.Json;
|
|
using X1.DynamicClientCore.Models;
|
|
|
|
namespace X1.DynamicClientCore.Core
|
|
{
|
|
/// <summary>
|
|
/// DynamicHttpClient 核心执行部分
|
|
/// 包含核心的HTTP请求执行逻辑
|
|
/// </summary>
|
|
public partial class DynamicHttpClient
|
|
{
|
|
/// <summary>
|
|
/// 执行HTTP请求的统一方法
|
|
/// </summary>
|
|
private async Task<T?> ExecuteRequestAsync<T>(string serviceName, string endpoint, HttpMethod method,
|
|
object? data, RequestOptions? options, CancellationToken cancellationToken = default)
|
|
{
|
|
_logger.LogDebug("开始执行异步请求: {ServiceName}:{Endpoint}, 方法: {Method}", serviceName, endpoint, method);
|
|
|
|
int requestTimeout = 0; // 声明超时变量,供异常处理使用
|
|
|
|
try
|
|
{
|
|
// 1. 获取服务端点
|
|
_logger.LogDebug("正在获取服务端点: {ServiceName}", serviceName);
|
|
var serviceEndpoint = _endpointManager.GetEndpoint(serviceName);
|
|
if (serviceEndpoint == null)
|
|
{
|
|
_logger.LogWarning("服务端点未找到: {ServiceName}", serviceName);
|
|
throw new DynamicHttpClientException(
|
|
$"服务 '{serviceName}' 未找到",
|
|
DynamicHttpClientExceptionType.ServiceNotFound,
|
|
serviceName,
|
|
endpoint);
|
|
}
|
|
|
|
if (!serviceEndpoint.Enabled)
|
|
{
|
|
_logger.LogWarning("服务端点已禁用: {ServiceName}", serviceName);
|
|
throw new DynamicHttpClientException(
|
|
$"服务 '{serviceName}' 已禁用",
|
|
DynamicHttpClientExceptionType.ServiceDisabled,
|
|
serviceName,
|
|
endpoint);
|
|
}
|
|
|
|
_logger.LogDebug("服务端点获取成功: {ServiceName}, URL: {FullUrl}, 超时: {Timeout}s",
|
|
serviceName, serviceEndpoint.GetFullUrl(), serviceEndpoint.Timeout);
|
|
|
|
// 2. 创建HTTP客户端
|
|
_logger.LogDebug("正在创建HTTP客户端");
|
|
var httpClient = _httpClientFactory.CreateClient();
|
|
requestTimeout = options?.Timeout ?? serviceEndpoint.Timeout;
|
|
httpClient.Timeout = TimeSpan.FromSeconds(requestTimeout);
|
|
_logger.LogDebug("HTTP客户端创建完成,超时设置: {Timeout}s", requestTimeout);
|
|
|
|
// 3. 构建请求URL
|
|
var url = $"{serviceEndpoint.GetFullUrl()}/{endpoint.TrimStart('/')}";
|
|
_logger.LogDebug("构建请求URL: {Url}", url);
|
|
var request = new HttpRequestMessage(method, url);
|
|
|
|
// 4. 添加请求头
|
|
if (options?.Headers != null)
|
|
{
|
|
_logger.LogDebug("添加自定义请求头,数量: {HeaderCount}", options.Headers.Count);
|
|
foreach (var header in options.Headers)
|
|
{
|
|
request.Headers.Add(header.Key, header.Value);
|
|
_logger.LogDebug("添加请求头: {Key} = {Value}", header.Key, header.Value);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 添加默认的JSON请求头
|
|
request.Headers.Add("Accept", "application/json");
|
|
_logger.LogDebug("添加默认JSON请求头: Accept = application/json");
|
|
}
|
|
|
|
// 5. 添加请求体
|
|
if (data != null && (method == HttpMethod.Post || method == HttpMethod.Put))
|
|
{
|
|
_logger.LogDebug("准备序列化请求数据,数据类型: {DataType}", data.GetType().Name);
|
|
try
|
|
{
|
|
var json = JsonConvert.SerializeObject(data);
|
|
_logger.LogDebug("请求数据序列化成功,长度: {JsonLength} 字符", json.Length);
|
|
request.Content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");
|
|
_logger.LogDebug("请求体设置完成");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "请求数据序列化失败: {ServiceName}:{Endpoint}", serviceName, endpoint);
|
|
throw new DynamicHttpClientException(
|
|
"请求数据序列化失败",
|
|
DynamicHttpClientExceptionType.SerializationError,
|
|
serviceName,
|
|
endpoint,
|
|
innerException: ex);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_logger.LogDebug("无请求体数据");
|
|
}
|
|
|
|
// 6. 获取熔断器并执行请求
|
|
HttpResponseMessage response;
|
|
|
|
if (options?.EnableCircuitBreaker == true)
|
|
{
|
|
_logger.LogDebug("启用熔断器保护");
|
|
var circuitBreaker = _circuitBreakerManager.GetOrCreateCircuitBreaker(
|
|
serviceEndpoint.Name, options?.CircuitBreaker);
|
|
_logger.LogDebug("熔断器获取成功: {ServiceName}", serviceEndpoint.Name);
|
|
|
|
_logger.LogInformation("发送请求(带熔断器): {Method} {Url}", method, url);
|
|
response = await circuitBreaker.ExecuteAsync(async () => await httpClient.SendAsync(request, cancellationToken));
|
|
}
|
|
else
|
|
{
|
|
_logger.LogDebug("跳过熔断器保护");
|
|
_logger.LogInformation("发送请求(无熔断器): {Method} {Url}", method, url);
|
|
response = await httpClient.SendAsync(request, cancellationToken);
|
|
}
|
|
|
|
_logger.LogDebug("收到响应: {StatusCode} {ReasonPhrase}", response.StatusCode, response.ReasonPhrase);
|
|
|
|
if (response.IsSuccessStatusCode)
|
|
{
|
|
_logger.LogDebug("开始读取响应内容");
|
|
var content = await response.Content.ReadAsStringAsync(cancellationToken);
|
|
_logger.LogDebug("响应内容读取完成,长度: {ContentLength} 字符", content.Length);
|
|
_logger.LogInformation("请求成功: {StatusCode}", response.StatusCode);
|
|
|
|
if (typeof(T) == typeof(string))
|
|
{
|
|
_logger.LogDebug("返回字符串类型响应");
|
|
return (T)(object)content;
|
|
}
|
|
|
|
_logger.LogDebug("开始反序列化响应数据,目标类型: {TargetType}", typeof(T).Name);
|
|
try
|
|
{
|
|
var result = JsonConvert.DeserializeObject<T>(content);
|
|
_logger.LogDebug("响应数据反序列化成功");
|
|
return result;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "响应数据反序列化失败: {ServiceName}:{Endpoint}, 目标类型: {TargetType}",
|
|
serviceName, endpoint, typeof(T).Name);
|
|
throw new DynamicHttpClientException(
|
|
"响应数据反序列化失败",
|
|
DynamicHttpClientExceptionType.SerializationError,
|
|
serviceName,
|
|
endpoint,
|
|
(int)response.StatusCode,
|
|
ex);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_logger.LogError("请求失败: {StatusCode} {ReasonPhrase}, URL: {Url}",
|
|
response.StatusCode, response.ReasonPhrase, url);
|
|
throw new DynamicHttpClientException(
|
|
$"HTTP请求失败: {response.StatusCode} {response.ReasonPhrase}",
|
|
DynamicHttpClientExceptionType.HttpRequestFailed,
|
|
serviceName,
|
|
endpoint,
|
|
(int)response.StatusCode);
|
|
}
|
|
}
|
|
catch (DynamicHttpClientException ex)
|
|
{
|
|
// 记录自定义异常,然后重新抛出
|
|
_logger.LogError(ex, "动态HTTP客户端异步异常: {ServiceName}:{Endpoint}, 类型: {ExceptionType}, 消息: {Message}",
|
|
serviceName, endpoint, ex.ExceptionType, ex.Message);
|
|
throw;
|
|
}
|
|
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
|
|
{
|
|
// 超时异常
|
|
_logger.LogWarning("异步请求超时: {ServiceName}:{Endpoint}, 超时时间: {Timeout}s",
|
|
serviceName, endpoint, requestTimeout);
|
|
throw new DynamicHttpClientException(
|
|
"请求超时",
|
|
DynamicHttpClientExceptionType.Timeout,
|
|
serviceName,
|
|
endpoint,
|
|
innerException: ex);
|
|
}
|
|
catch (OperationCanceledException ex)
|
|
{
|
|
// 请求被取消
|
|
_logger.LogInformation("异步请求被取消: {ServiceName}:{Endpoint}", serviceName, endpoint);
|
|
throw new DynamicHttpClientException(
|
|
"请求被取消",
|
|
DynamicHttpClientExceptionType.RequestCanceled,
|
|
serviceName,
|
|
endpoint,
|
|
innerException: ex);
|
|
}
|
|
catch (HttpRequestException ex)
|
|
{
|
|
// HTTP请求异常
|
|
_logger.LogError(ex, "HTTP异步请求异常: {ServiceName}:{Endpoint}, 消息: {Message}",
|
|
serviceName, endpoint, ex.Message);
|
|
throw new DynamicHttpClientException(
|
|
"网络请求失败",
|
|
DynamicHttpClientExceptionType.NetworkError,
|
|
serviceName,
|
|
endpoint,
|
|
innerException: ex);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// 其他未知异常
|
|
_logger.LogError(ex, "异步请求未知异常: {ServiceName}:{Endpoint}, 异常类型: {ExceptionType}, 消息: {Message}",
|
|
serviceName, endpoint, ex.GetType().Name, ex.Message);
|
|
throw new DynamicHttpClientException(
|
|
"请求执行失败",
|
|
DynamicHttpClientExceptionType.Unknown,
|
|
serviceName,
|
|
endpoint,
|
|
innerException: ex);
|
|
}
|
|
finally
|
|
{
|
|
_logger.LogDebug("异步请求执行完成: {ServiceName}:{Endpoint}", serviceName, endpoint);
|
|
}
|
|
}
|
|
}
|
|
}
|