介绍个 HTTP Client 中常用于记录请求以及响应的中间建:System.Net.Http.DelegatingHandler 文档 ,不废话直接看代码
声明:全篇基于 .NET 7 编写
点击查看代码
public sealed class LoggingHandler : DelegatingHandler
{
public LoggingHandler() : base()
{
}
public LoggingHandler(HttpMessageHandler innerHandler) : base(innerHandler)
{
}
/// <summary>
/// 同步请求拦截器
/// </summary>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
protected override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken)
{
StringBuilder logBuilder = new(Environment.NewLine);
Uri? uri = request.RequestUri;
logBuilder.AppendLine($"\t== request uri: {uri} at {DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}");
logBuilder.AppendLine($"\t=- request method: {request.Method}");
logBuilder.AppendLine($"\t=- request headers: {request.Headers.IObjectToJson()}");
// 仅记录request.Content 是StringContent 的请求参数
string requestContent = "";
if (request.Content.GetType() == typeof(StringContent))
{
requestContent = request.Content.ReadAsStringAsync().Result;
}
logBuilder.AppendLine($"\t=@ request body: {requestContent}");
HttpResponseMessage responseMessage = base.Send(request, cancellationToken);
logBuilder.AppendLine($"\t=+ response headers: {responseMessage.Headers.IObjectToJson()}");
if (!responseMessage.IsSuccessStatusCode)
{
string responseString = $"异常:{responseMessage.StatusCode}";
// 非空时,写入响应异常信息
logBuilder.Append($"\t=~ response error result: {responseString}");
}
// 日志记录,可替换为其他日志记录方式
Log4Helper.WriteApiRequest(logBuilder.ToString(), callerMemberName);
return responseMessage;
}
/// <summary>
/// 异步请求拦截器
/// </summary>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
StringBuilder logBuilder = new(Environment.NewLine);
Uri? uri = request.RequestUri;
logBuilder.AppendLine($"\t== async request uri: {uri} at {DateTime.Now:G}");
logBuilder.AppendLine($"\t=- async request headers: {request.Headers.IObjectToJson()}");
// 非json 请求,不记录请求 body
string requestContent = "";
if (request.Content?.GetType() == typeof(StringContent))
{
requestContent = request.Content.ReadAsStringAsync().Result;
}
logBuilder.AppendLine($"\t=@ request body: {requestContent}");
// 添加 .ConfigureAwait(false) 避免同步线程上下文
HttpResponseMessage responseMessage = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
logBuilder.AppendLine($"\t=+ async response headers: {responseMessage.Headers.IObjectToJson()}");
if (!responseMessage.IsSuccessStatusCode)
{
string responseString = $"异常:{responseMessage.StatusCode}";
// 非空时,写入响应异常信息
logBuilder.Append($"\t=~ response error result: {responseString}");
}
Log4Helper.WriteApiRequest(logBuilder.ToString());
return responseMessage;
}
}
示例中 LoggingHandler 继承自 System.Net.Http.DelegatingHandler
从上往下看,可以看到构造函数包含一个无参构造函数,以及一个有 HttpMessageHandler
的 构造函数,
在发送请求的顺序上来看,通常情况下 LoggingHandler
是作为最终的handler使用,
所以需要将内部handler传入,
使用(此处使用 直接 new 一个 新的HttpClient 实际情况按需处理):
点击查看代码
public sealed class HttpHelper
{
private static readonly HttpMessageHandler handler = new HttpClientHandler()
{
Proxy = null,
AutomaticDecompression = DecompressionMethods.GZip,
SslProtocols =
System.Security.Authentication.SslProtocols.Tls
| System.Security.Authentication.SslProtocols.Tls12
| System.Security.Authentication.SslProtocols.Tls13
};
protected static readonly HttpMessageHandler loggerHandler = new LoggingHandler(handler);
HttpClient newhttpClient = new(handler: loggerHandler, disposeHandler: false)
{
BaseAddress = new Uri(Host),
// 设置超时时间
Timeout = new TimeSpan(TimeSpan.TicksPerMillisecond * TimeoutInMilliSeconds),
};
}