WebClient 似乎会自行添加请求头。发生在哪里?

问题描述 投票:0回答:1

这是一个失败的测试:

// 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 页面

java spring-webflux
1个回答
0
投票

当准备发送请求时,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
标头或其他标头来处理请求。

© www.soinside.com 2019 - 2024. All rights reserved.