RestSharp 拦截器(Interceptor)完整指南:在请求与响应生命周期中嵌入自定义逻辑
【免费下载链接】RestSharpSimple REST and HTTP API Client for .NET项目地址: https://gitcode.com/gh_mirrors/re/RestSharp
导读
RestSharp 的拦截器(Interceptor)机制允许开发者在请求发送前、响应返回后的各个生命周期节点插入自定义逻辑,用于统一添加请求头、改写请求体、校验或修正响应,甚至中止一次请求。本指南以 RestSharp 113 及后续版本文档为核心,结合仓库源码与集成测试,讲解拦截器的五种可重写方法、客户端级与请求级的注册方式、执行顺序,以及如何利用CompatibilityInterceptor平滑迁移旧版请求钩子。读完本文,你将能够在自己的 .NET 项目中熟练编写、注册并调试 RestSharp 拦截器。
什么是拦截器:贯穿请求生命周期的钩子
RestSharp 的拦截器是一组在 HTTP 请求组装、发送、响应接收与反序列化等阶段被回调的钩子。它相比传统"请求参数"能力更通用——不仅可以修改请求本身,还可以访问并修改底层HttpRequestMessage/HttpResponseMessage,这是普通参数 API 无法触及的层面。
文档明确列出了拦截器的适用场景:
- 在请求发出前添加自定义 Header;
- 修改请求体(Body);
- 取消请求;
- 在响应返回给调用方之前修改响应。
拦截器的基类位于 Interceptor.cs,是RestSharp.Interceptors命名空间下的一个抽象类,所有钩子方法均以virtual声明并带有空实现,因此你只需要继承它并按需覆写感兴趣的方法即可。
五种可重写方法及其生命周期位置
根据 Interceptor.cs 的源码与 XML 注释,你可以覆写以下方法:
| 方法 | 触发时机 | 参数 |
|---|---|---|
BeforeRequest | 在组装请求消息(compose request message)之前,即请求参数校验、认证与 URL 构建之前 | RestRequest request, CancellationToken |
BeforeHttpRequest | HttpRequestMessage发送给服务器之前 | HttpRequestMessage, CancellationToken |
AfterHttpRequest | 从远程服务器收到HttpResponseMessage之后、尚未包装成RestResponse之前 | HttpResponseMessage, CancellationToken |
AfterRequest | RestResponse从HttpResponseMessage构建完成之后 | RestResponse, CancellationToken |
BeforeDeserialization | 反序列化开始之前(仅使用泛型ExecuteAsync<T>等泛型执行方法时触发) | RestResponse, CancellationToken |
所有方法都必须返回ValueTask实例。
在源码中的实际调用点
将上述方法映射到请求执行主链路,可以从 RestClient.Async.cs 的ExecuteRequestAsync中看到它们的真实执行位置与先后顺序:
CombineInterceptors(request)——合并客户端级与请求级拦截器(见下文"执行顺序");OnBeforeRequest(request, ct)——调用所有拦截器的BeforeRequest;- 请求参数校验、认证器执行;
- 构建 URL 与
HttpRequestMessage(含 Content、Host、CacheControl、Headers 等); - 旧版
request.OnBeforeRequest(Obsolete 钩子)执行; OnBeforeHttpRequest(request, message, ct)——调用BeforeHttpRequest;- 发送请求(含重定向处理
SendWithRedirectsAsync); - 旧版
request.OnAfterRequest(Obsolete 钩子)执行; OnAfterHttpRequest(request, responseMessage, ct)——调用AfterHttpRequest;- 组装
RestResponse; OnAfterRequest(response, ct)——调用AfterRequest。
反序列化阶段则位于 RestSerializers.cs:泛型反序列化前先调用所有拦截器的BeforeDeserialization,再执行旧版request.OnBeforeDeserialization,最后进入DeserializeContent<T>。
需要特别留意的是BeforeDeserialization的触发条件:基类注释明确说明"won't be called if using non-generic ExecuteAsync",即只有使用ExecuteAsync<T>这类泛型执行方法、真正发生反序列化时才会回调,这与测试用例InterceptorTests中使用ExecutePostAsync<TestResponse>验证该方法的做法一致。
实现一个拦截器:从加 Header 的示例说起
文档给出了一个最经典的示例——在请求发出前为HttpRequestMessage添加 Header:
// This interceptor adds a header to the request // You'd not normally use this interceptor, as RestSharp already has a method // to add headers to the request class HeaderInterceptor(string headerName, string headerValue) : Interceptors.Interceptor { public override ValueTask BeforeHttpRequest(HttpRequestMessage requestMessage, CancellationToken cancellationToken) { requestMessage.Headers.Add(headerName, headerValue); return ValueTask.CompletedTask; } }几个值得展开的要点:
- 主构造函数:示例使用了 C# 12 的主构造函数语法(
string headerName, string headerValue),直接把配置参数注入拦截器,让拦截器实例可复用、可配置; - 直接操作
HttpRequestMessage.Headers:BeforeHttpRequest拿到的是即将发送的真实 HTTP 消息,因此可以修改 Headers、改写 Content、甚至调整 URI; - 返回
ValueTask.CompletedTask:同步逻辑用ValueTask.CompletedTask表示已完成;因为方法返回ValueTask,你完全可以在方法体内使用async/await,把异步操作(如调用远程配置服务、读取密钥)嵌入生命周期; - 命名空间:示例中的
Interceptors.Interceptor即RestSharp.Interceptors.Interceptor,仓库里 RestSharp.csproj 下该命名空间默认可用。
说明:文档特意提醒,普通场景下添加请求头不必写拦截器——RestSharp 本身就提供了
AddHeader等参数 API。拦截器的价值在于"横切"逻辑:多个请求、多个客户端共享同一套处理规则。
一个完整的"日志 + 校验"拦截器示例
结合源码中TestInterceptor(TestInterceptor.cs)的覆写风格,一个同时覆盖多个阶段、使用异步逻辑的拦截器大致如下:
class LoggingInterceptor : Interceptors.Interceptor { public override async ValueTask BeforeRequest(RestRequest request, CancellationToken cancellationToken) { Console.WriteLine($"[BeforeRequest] {request.Method} {request.Resource}"); await Task.CompletedTask; } public override async ValueTask BeforeHttpRequest(HttpRequestMessage requestMessage, CancellationToken cancellationToken) { Console.WriteLine($"[BeforeHttpRequest] {requestMessage.Method} {requestMessage.RequestUri}"); // 例如在这里改写请求体: // requestMessage.Content = new StringContent("...", Encoding.UTF8, "application/json"); await Task.CompletedTask; } public override ValueTask AfterHttpRequest(HttpResponseMessage responseMessage, CancellationToken cancellationToken) { Console.WriteLine($"[AfterHttpRequest] {(int)responseMessage.StatusCode}"); return base.AfterHttpRequest(responseMessage, cancellationToken); } public override ValueTask AfterRequest(RestResponse response, CancellationToken cancellationToken) { Console.WriteLine($"[AfterRequest] {response.ResponseStatus}"); return base.AfterRequest(response, cancellationToken); } public override ValueTask BeforeDeserialization(RestResponse response, CancellationToken cancellationToken) { Console.WriteLine($"[BeforeDeserialization] content length: {response.Content?.Length}"); return base.BeforeDeserialization(response, cancellationToken); } }这里体现了基类设计的一个重要细节:每个虚方法默认返回一个已完成的ValueTask,所以只覆写你关心的方法即可,其余阶段自动"空转";覆写后如果不需要自定义返回值,直接return base.XXX(...)是安全且推荐的做法(与TestInterceptor的实现一致)。
注册拦截器:客户端级、请求级与执行顺序
文档强调:拦截器可以按需添加任意多个,既可以挂在客户端上,也可以挂在单个请求上,所有拦截器按照添加顺序执行。
客户端级注册(作用于该客户端的所有请求)
通过RestClientOptions.Interceptors集合注入:
var options = new RestClientOptions("https://api.example.com") { Interceptors = [new HeaderInterceptor("Authorization", token)] }; var client = new RestClient(options);在 RestClientOptions.cs 中,Interceptors被定义为List<Interceptor>,默认值为空集合[]。客户端级拦截器会对该客户端发起的每一个请求生效——典型的全局横切逻辑(如统一鉴权、统一日志、统一错误上报)都应放在这里。
请求级注册(仅作用于单个请求)
var request = new RestRequest("resource") { Interceptors = [new HeaderInterceptor("Authorization", token)] };在 RestRequest.cs 中,请求级Interceptors是List<Interceptor>?,默认可为空。此方式适合只对特定请求生效的一次性逻辑。
两者组合时的真实执行顺序
当请求同时存在请求级与客户端级拦截器时,两者的合并逻辑在 RestClient.Async.cs 的CombineInterceptors中实现:
void CombineInterceptors(RestRequest request) { if (request.Interceptors == null) { if (Options.Interceptors == null) return; request.Interceptors = Options.Interceptors.ToList(); return; } if (Options.Interceptors != null) { request.Interceptors.AddRange(Options.Interceptors); } }从源码可以推断出两条规则:
- 若请求未设置拦截器,则直接使用客户端拦截器列表的副本(
ToList()保证不共享引用、避免并发修改问题); - 若请求已设置拦截器,则把客户端拦截器追加到请求拦截器之后。
因此实际执行顺序为:先按添加顺序执行请求级拦截器,再按添加顺序执行客户端级拦截器。每个阶段(BeforeRequest、BeforeHttpRequest等)都是按这个合并后的顺序逐一遍历执行(见 RestClient.Async.cs 中四个OnXXX静态方法的foreach循环)。集成测试 InterceptorTests.cs 的Should_call_both_client_and_request_interceptors用例专门验证了客户端与请求拦截器会被同时调用。
关于"取消请求"
文档提到拦截器可以取消请求。从执行链路看,所有拦截器方法都接收CancellationToken,且每个阶段的回调是顺序await的——如果某个拦截器抛出异常(例如抛出一个OperationCanceledException或业务异常),执行链路会立即中断。测试ThrowExceptionIn_InterceptBeforeRequest(InterceptorTests.cs)证实:在BeforeRequest中抛异常后,BeforeHttpRequest、AfterHttpRequest、AfterRequest、BeforeDeserialization均不会被调用。你可以在拦截器中结合业务条件抛出异常来达到"中止本次请求"的效果。
BeforeDeserialization:反序列化前的最后一道关卡
BeforeDeserialization是五个方法中最特殊的一个,它在反序列化真正发生之前回调,可以拿到尚未转换为目标类型的原始RestResponse。适用场景包括:
- 在解析前校验响应状态,决定是否丢弃或改写内容;
- 对响应内容做预处理(如解密、解包、字符集修正);
- 记录原始响应以便调试。
其源码执行点在 RestSerializers.cs:泛型反序列化入口Deserialize<T>中先await OnBeforeDeserialization(raw, ct),再进入内容解析。需要再次强调:只有泛型执行方法(如ExecuteAsync<T>、ExecuteGetAsync<T>)才会触发此钩子;非泛型的ExecuteAsync不会走反序列化路径,因此该钩子不会被调用。
从旧版请求钩子迁移:CompatibilityInterceptor
RestSharp 111.0 之前的请求钩子(OnBeforeRequest、OnAfterRequest、OnBeforeDeserialization)已在源码中标记为Obsolete(见 RestRequest.cs,弃用信息统一为"Use Interceptors instead"),并将在未来版本移除。为了降低迁移成本,RestSharp 提供了 CompatibilityInterceptor.cs,它把旧钩子包装成拦截器属性,让你在不改变原有业务逻辑的前提下完成迁移。
迁移前后对照
文档给出的例子——旧代码使用OnBeforeDeserialization钩子:
var request = new RestRequest("success"); request.OnBeforeDeserialization += _ => throw new Exception(exceptionMessage);迁移为拦截器写法:
var request = new RestRequest("success") { Interceptors = [new CompatibilityInterceptor { OnBeforeDeserialization = _ => throw new Exception(exceptionMessage) }] };CompatibilityInterceptor 支持的三个属性
从源码可以看到,CompatibilityInterceptor提供了与旧钩子一一对应的三个属性,并在内部把它们桥接到对应阶段的拦截器方法:
| 旧钩子(已弃用) | CompatibilityInterceptor 属性 | 桥接的拦截器方法 |
|---|---|---|
OnBeforeRequest(Func<HttpRequestMessage, ValueTask>) | OnBeforeRequest | BeforeHttpRequest |
OnAfterRequest(Func<HttpResponseMessage, ValueTask>) | OnAfterRequest | AfterHttpRequest |
OnBeforeDeserialization(Action<RestResponse>) | OnBeforeDeserialization | BeforeDeserialization |
注意一个易混淆点:旧OnBeforeRequest的签名参数是HttpRequestMessage,对应的是新拦截器的BeforeHttpRequest(而非BeforeRequest);旧OnAfterRequest参数是HttpResponseMessage,对应AfterHttpRequest。迁移时请对照上表,避免把委托挂错阶段。BeforeRequest与AfterRequest这两个操作RestRequest/RestResponse的阶段是拦截器新增的能力,旧钩子中没有直接对应物。
验证与调试:仓库中的测试与实现路径
如果你希望在集成到自己的项目前理解拦截器的完整行为,仓库里已经有现成的测试与示例可参考:
- 拦截器基类:Interceptor.cs —— 五个虚方法的默认实现与触发时机注释;
- 兼容迁移类:CompatibilityInterceptor.cs —— 旧钩子到拦截器的桥接实现;
- 执行链路:RestClient.Async.cs 与 RestSerializers.cs —— 各阶段回调的真实调用位置;
- 配置入口:RestClientOptions.cs(客户端级)、RestRequest.cs(请求级);
- 集成测试:InterceptorTests.cs 与 TestInterceptor.cs —— 覆盖"客户端级与请求级同时调用""异常中断后续阶段"等关键行为,可直接作为你编写自测用例的模板。
小结
RestSharp 拦截器是围绕请求生命周期设计的横切扩展点:BeforeRequest→BeforeHttpRequest→(发送)→AfterHttpRequest→AfterRequest→BeforeDeserialization五个阶段分别覆盖了"高层请求对象""底层 HTTP 消息""原始响应消息""包装后的 RestResponse"和"反序列化前"五个视角,配合客户端级与请求级两级注册、按添加顺序执行的规则,足以应对鉴权、日志、改写、校验、中止请求等绝大多数横切需求。若你的代码仍在使用 111.0 之前的旧钩子,应尽快通过CompatibilityInterceptor迁移,为旧钩子的最终移除提前做好准备。
【免费下载链接】RestSharpSimple REST and HTTP API Client for .NET项目地址: https://gitcode.com/gh_mirrors/re/RestSharp
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考