如何使用 WebFlux 和 Netty HttpClient 访问请求 body
How to access to request body using WebFlux and Netty HttpClient
我需要使用 Webflux 的 WebClient 计算请求的某种摘要 body,并且此摘要必须设置为 HTTP header。使用旧的 Spring MVC ClientHttpRequestInterceptor 很容易,因为请求 body 是作为字节数组提供的。
ExchangeFilterFunction 不提供对请求的访问 body。
body 作为 JSon 发送,Spring 使用 Jackson 序列化 Java objects,因此可以选择序列化我的 Object 转化为 Json 并在其上计算摘要,但这种策略有两个缺点:
- 我的代码会重复 Spring 在实际发送请求时将执行的操作
- 不能保证 Spring 作为请求发送的实际字节等于我传递给摘要函数的字节
我想我应该使用 Netty 的一些低级 API,但我找不到任何示例。
目前使用 WebClient 不容易做到这一点。但是有一些方法可以通过拦截 body post-serialization 来做到这一点。这可以通过注册自定义编码器来完成,该编码器在编码后拦截数据,并将其传递给自定义 HttpConnector 以将其作为 header.
注入
这篇博客 post 解释了一种实现方法:https://andrew-flower.com/blog/Custom-HMAC-Auth-with-Spring-WebClient
编辑:目前这个博客post没有考虑并发请求。有关修改后的方法,请参阅 Claodio 接受的答案。
我实施了@rewolf 提出的解决方案并且它有效,但由于 WebFlux 的多线程特性,我遇到了一个问题。
事实上,客户端请求有可能被一个线程保存到线程本地映射中,但另一个线程试图获取它,因此返回空值。
例如,如果要签名的请求是在以 Mono 作为请求主体参数的 Rest 控制器方法中创建的,则会发生这种情况:
@PostMapping
public String execute(@RequestBody Mono<MyBody> body){
Mono<OtherBody> otherBody = body.map(this::transformBodyIntoOtherBody);
...
webClient.post()
.body(otherBody)
.exchange();
...
}
根据 Reactor 规范,应使用 Reactor Context 而不是 Thread Local。
我 fork @rewolf 项目并实现了一个基于 Reactor Context 的解决方案:https://github.com/taxone/blog-hmac-auth-webclient
我需要使用 Webflux 的 WebClient 计算请求的某种摘要 body,并且此摘要必须设置为 HTTP header。使用旧的 Spring MVC ClientHttpRequestInterceptor 很容易,因为请求 body 是作为字节数组提供的。
ExchangeFilterFunction 不提供对请求的访问 body。
body 作为 JSon 发送,Spring 使用 Jackson 序列化 Java objects,因此可以选择序列化我的 Object 转化为 Json 并在其上计算摘要,但这种策略有两个缺点:
- 我的代码会重复 Spring 在实际发送请求时将执行的操作
- 不能保证 Spring 作为请求发送的实际字节等于我传递给摘要函数的字节
我想我应该使用 Netty 的一些低级 API,但我找不到任何示例。
目前使用 WebClient 不容易做到这一点。但是有一些方法可以通过拦截 body post-serialization 来做到这一点。这可以通过注册自定义编码器来完成,该编码器在编码后拦截数据,并将其传递给自定义 HttpConnector 以将其作为 header.
注入这篇博客 post 解释了一种实现方法:https://andrew-flower.com/blog/Custom-HMAC-Auth-with-Spring-WebClient
编辑:目前这个博客post没有考虑并发请求。有关修改后的方法,请参阅 Claodio 接受的答案。
我实施了@rewolf 提出的解决方案并且它有效,但由于 WebFlux 的多线程特性,我遇到了一个问题。
事实上,客户端请求有可能被一个线程保存到线程本地映射中,但另一个线程试图获取它,因此返回空值。
例如,如果要签名的请求是在以 Mono 作为请求主体参数的 Rest 控制器方法中创建的,则会发生这种情况:
@PostMapping
public String execute(@RequestBody Mono<MyBody> body){
Mono<OtherBody> otherBody = body.map(this::transformBodyIntoOtherBody);
...
webClient.post()
.body(otherBody)
.exchange();
...
}
根据 Reactor 规范,应使用 Reactor Context 而不是 Thread Local。
我 fork @rewolf 项目并实现了一个基于 Reactor Context 的解决方案:https://github.com/taxone/blog-hmac-auth-webclient