delegatinghandler 必须传非 null innerhandler,否则 sendasync 时抛 argumentnullexception;注册需用 addhttpmessagehandler() 而非直接注册服务;sendasync 中必须传递 cancellationtoken;禁止继承已过时的 httpmessagehandler。

DelegatingHandler 必须传 innerHandler,否则运行时直接崩——这不是警告,是构造契约。
为什么 new LoggingHandler() 会抛 ArgumentNullException
DelegatingHandler 的 InnerHandler 属性不是可选配置项,而是管道链存在的前提。它的基类逻辑在 SendAsync 中第一行就检查:if (InnerHandler == null) throw new ArgumentNullException("innerHandler")。
- 错误写法:
new LoggingHandler()(无参构造),即使编译通过,首次调用SendAsync就崩溃 - 正确写法:必须显式传入非 null 的
HttpMessageHandler,比如new LoggingHandler(new HttpClientHandler())或上一级 handler 实例 - 在 DI 场景下(如
AddHttpClient),AddHttpMessageHandler<t>()</t>会自动注入正确的InnerHandler,你不用手动 new
注册 DelegatingHandler 时别漏掉 AddHttpMessageHandler
直接把 DelegatingHandler 子类注册为服务(如 services.AddSingleton<logginghandler>()</logginghandler>)毫无意义——HttpClient 根本不会用它。
- 必须用
AddHttpMessageHandler<logginghandler>()</logginghandler>注册,DI 才会在构建HttpClient实例时把它插入管道 - 多个 handler 按注册顺序从前到后串联,先注册的更靠近请求发起端(比如日志 handler 应早于重试 handler)
- 如果 handler 需要依赖其他服务(如
ILogger),确保构造函数参数能被 DI 解析;AddHttpMessageHandler支持带生命周期的服务注入
SendAsync 中不传递 cancellationToken 是隐蔽的线程陷阱
重写 SendAsync 时若忽略 cancellationToken,会导致请求无法取消,UI 冻结、后台任务卡死、超时失效等连锁问题。
- 错误示范:
return base.SendAsync(request, CancellationToken.None)—— 彻底丢弃上游传入的 token - 正确做法:始终将参数原样传给
base.SendAsync,除非你明确要在某一步做 cancel 响应(比如提前终止重试) - 若需在拦截逻辑中异步耗时(如记录日志),用
Task.Run(() => LogAsync(...))或LogAsync(...).ConfigureAwait(false)避免阻塞主线程
别继承 HttpMessageHandler,那是给 SocketsHttpHandler 准备的
看到 “自定义 HTTP 处理器” 就去写 public class MyHandler : HttpMessageHandler?这等于绕过整个 .NET HTTP 管道设计,亲手拆掉连接池、TLS 复用、DNS 缓存这些基础设施。
-
HttpMessageHandler是底层传输实现的基类,.NET 6+ 已标记为[Obsolete],官方文档明确不建议继承 -
DelegatingHandler才是专为业务拦截设计的抽象层,它复用默认连接管理,只负责“前后钩子”逻辑 - 继承
HttpMessageHandler后,HttpClient会为你新建独立连接池,高频请求下极易触发SocketException: Too many open files
真正难的不是写一个 handler,而是让它不破坏默认行为——连接复用、取消传播、异常透传,这些细节一旦出错,问题往往延迟暴露,排查成本远高于初期多写两行构造代码。










