这是一个失败的测试:
// these are pretty basic dependencies so I won't include imports or pom
@Test
void testIfNoHeadersAreAddedToRequestImplicitly() {
Mono<Map<String, Object>> responseMono = WebClient.builder()
.baseUrl("https://httpbin.org")
.build()
.get()
.uri("/headers")
.retrieve()
.bodyToMono(new ParameterizedTypeReference<>() {});
StepVerifier.create(responseMono.map(m -> m.get("headers")).cast(Map.class))
.expectNextMatches(Map::isEmpty)
.verifyComplete();
}
我很确定
WebClient
添加了一些默认请求标头,例如 HOST
,我想 👀 see 它发生在哪里。碰巧我需要禁用此功能,但我同样渴望👀看到原因
到目前为止,我对这个问题的调试并不成功 - 包括对
DefaultWebClient.exchange()
的调试,它似乎按预期工作
但是,我发现一个错误的请求被传递给了这个回调
// org.springframework.http.client.reactive.ReactorClientHttpConnector.connect(..)
return requestSender
// put a breakpoint on the lambda
.send((request, outbound) -> requestCallback.apply(adaptRequest(method, uri, request, outbound)))
当我深入挖掘时,Netty 只是假装这些标头一直存在(一旦出现任何请求,它就已经具有这些默认标头)
这个问题也用 WireMock 重现,所以它不太可能与
httpbin
有任何关系(顺便说一句,如果你不熟悉它,你可以访问 API 页面)
当准备发送请求时,Netty 显式添加默认标头。主要兴趣点在这里:
// io.netty.handler.codec.http.HttpHeaders
public interface HttpHeaders extends Iterable<Map.Entry<String, String>> {
HttpHeaders add(String name, Object value);
// ...
}
您可能知道,Spring 的
WebClient
在底层使用 Project Reactor 和 Netty 来发出 Web 请求。当您创建 WebClient
实例并使用它构建请求时,标头不会立即显示。然而,一旦请求即将发出(当 Netty 的 HttpClientOperations
发挥作用时),Netty 就会添加这些默认标头(如 HOST、Accept-Encoding 等),即使您没有明确设置这些标头。
以下是 GitHub 上 Netty 的
HttpClientOperations
的部分内容:
// io.netty.handler.codec.http.HttpClientOperations
@Override
protected void onOutboundSubscribe(Subscriber<? super ByteBuf> s) {
if (!HttpRequestEncoder.encoderHeader(headers(), method(), uri())) {
if (log.isDebugEnabled()) {
log.debug(format(ctx.channel(), "Dropped {}"), this);
}
return;
}
//...
}
HttpRequestEncoder.encoderHeader(...)
部分是准备和添加标头(如果标头不存在)的地方。您也可以查看 encodeHeaders
方法以获得更深入的见解。
这是
HttpClientOperations
中添加标头的时间点:
// io.netty.handler.codec.http.HttpHeaders
public interface HttpHeaders extends Iterable<Map.Entry<String, String>> {
HttpHeaders add(String name, Object value);
// ...
}
不幸的是,
WebClient
没有提供开箱即用的解决方案来阻止Netty添加这些标头。但是,您可以创建自定义过滤器并将其添加到您的 WebClient
中,以便在每次构建请求时有意删除这些标头:
WebClient.builder()
.baseUrl("https://httpbin.org")
.filter((clientRequest, next) -> {
ClientRequest mutatedRequest = ClientRequest.from(clientRequest)
.headers(httpHeaders -> {
httpHeaders.remove("Host");
// remove other headers if needed
})
.build();
return next.exchange(mutatedRequest);
})
.build()
.get()
// continue with your code for .uri, etc.
这样,每次您构建请求时,过滤器都会在发送请求之前删除 Host 标头(如果您愿意,还可以删除其他标头)。请注意,这可能不是最佳实践,因为某些服务器可能需要
Host
标头或其他标头来处理请求。