如何为HttpClient扩展方法添加绑定到具体实例的EventHandler事件?
HttpClient实例级错误事件实现方案
需求完全可以实现,以下是两种常用的实现方式:
方案1:使用ConditionalWeakTable绑定实例与事件
该方案不需要修改原有HttpClient的创建逻辑,直接通过扩展方法实现事件与实例的绑定,使用ConditionalWeakTable做弱引用存储,不会阻碍HttpClient实例的正常垃圾回收。
实现代码
public static class HttpClientExtensions { // 弱引用映射表,键为HttpClient实例,值为对应绑定的错误事件委托 private static readonly ConditionalWeakTable<HttpClient, EventHandler<HttpClientErrorEventArgs>> _errorEventMap = new(); // 扩展方法:为当前HttpClient实例绑定错误处理事件 public static void AddErrorHandler(this HttpClient client, EventHandler<HttpClientErrorEventArgs> handler) { _errorEventMap.AddOrUpdate(client, handler); } public async static Task<T> GetSomthingSpecialAsync<T>(this HttpClient client, string url) { using var response = await client.GetAsync(url); if (response.StatusCode != System.Net.HttpStatusCode.OK) { // 触发当前实例绑定的错误事件 if (_errorEventMap.TryGetValue(client, out var errorHandler)) { errorHandler(client, new HttpClientErrorEventArgs() { StatusCode = response.StatusCode, Message = $"{response.StatusCode} {(int)response.StatusCode}" }); } return default; } response.EnsureSuccessStatusCode(); // 剩余业务逻辑 // [... ] } } public class HttpClientErrorEventArgs : EventArgs { public System.Net.HttpStatusCode StatusCode { get; set; } public string Message { get; set; } }
使用方式
// 不同HttpClient实例可绑定独立的错误处理逻辑 var clientA = new HttpClient(); clientA.AddErrorHandler((sender, e) => { Console.WriteLine($"客户端A请求错误:{e.Message}"); }); var clientB = new HttpClient(); clientB.AddErrorHandler((sender, e) => { Console.WriteLine($"客户端B请求错误:{e.Message}"); });
方案2:自定义HttpMessageHandler(官方推荐)
这是.NET官方推荐的扩展HttpClient行为的实现方式,所有走该Handler的请求都会自动触发事件,无需在每个扩展方法中重复编写事件触发逻辑,可维护性更高。
实现代码
// 自定义请求处理Handler,内置错误事件 public class ErrorCaptureHandler : DelegatingHandler { public event EventHandler<HttpClientErrorEventArgs> ErrorOccurred; public ErrorCaptureHandler(HttpMessageHandler innerHandler = null) { InnerHandler = innerHandler ?? new HttpClientHandler(); } protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var response = await base.SendAsync(request, cancellationToken); if (!response.IsSuccessStatusCode) { ErrorOccurred?.Invoke(request, new HttpClientErrorEventArgs { StatusCode = response.StatusCode, Message = $"{response.StatusCode} {(int)response.StatusCode}" }); } return response; } } // 你原有的扩展方法不需要再处理事件触发逻辑 public static class HttpClientExtensions { public async static Task<T> GetSomthingSpecialAsync<T>(this HttpClient client, string url) { using var response = await client.GetAsync(url); if (response.StatusCode != System.Net.HttpStatusCode.OK) { return default; } response.EnsureSuccessStatusCode(); // 剩余业务逻辑 // [... ] } } public class HttpClientErrorEventArgs : EventArgs { public System.Net.HttpStatusCode StatusCode { get; set; } public string Message { get; set; } }
使用方式
// 创建HttpClient时传入自定义Handler,每个实例绑定独立事件 var handlerA = new ErrorCaptureHandler(); handlerA.ErrorOccurred += (sender, e) => Console.WriteLine($"客户端A请求错误:{e.Message}"); var clientA = new HttpClient(handlerA); var handlerB = new ErrorCaptureHandler(); handlerB.ErrorOccurred += (sender, e) => Console.WriteLine($"客户端B请求错误:{e.Message}"); var clientB = new HttpClient(handlerB);
内容的提问来源于stack exchange,提问作者Chris Berlin
相关产品推荐
相关产品推荐

