缺少使用 WebClient 发送 POST 请求的 Content-Length 标头(SpringBoot 2.0.2.RELEASE)
Posted
技术标签:
【中文标题】缺少使用 WebClient 发送 POST 请求的 Content-Length 标头(SpringBoot 2.0.2.RELEASE)【英文标题】:Missing Content-Length header sending POST request with WebClient (SpringBoot 2.0.2.RELEASE) 【发布时间】:2018-11-02 17:02:27 【问题描述】:我正在使用 WebClient (SpringBoot 2.0.2.RELEASE) 发送带有 SOAP 请求的 POST,但它缺少所需的“Content-Length”标头旧版 API。
是否可以将 WebClient 配置为包含“Content-Length”标头? 在 SpringBoot 2.0.1 中为 EncoderHttpMessageWriter 解析并引入了 Spring Framework Issue,但它似乎不适用于 JAXB。
我尝试使用BodyInserters
:
webClient.post().body(BodyInserters.fromObject(request)).exchange();
和syncBody
:
webClient.post().syncBody(request).exchange();
他们都没有为WebClient
工作。但是,当使用RestTemplate
时,Content-Length 已设置并且 API 响应成功
【问题讨论】:
【参考方案1】:我正在努力解决同样的问题,作为一个丑陋的解决方法,我手动序列化请求(在我的情况下为 JSON)并设置长度(Kotlin 代码):
open class PostRetrieverWith411ErrorFix(
private val objectMapper: ObjectMapper
)
protected fun <T : Any> post(webClient: WebClient, body: Any, responseClass: Class<T>): Mono<T>
val bodyJson = objectMapper.writeValueAsString(body)
return webClient.post()
.contentType(MediaType.APPLICATION_JSON_UTF8)
.contentLength(bodyJson.toByteArray(Charset.forName("UTF-8")).size.toLong())
.syncBody(bodyJson)
.retrieve()
.bodyToMono(responseClass)
【讨论】:
目前看来只能这样了。经过一番研究,似乎没有办法使用当前的 webflux 实现来添加这个标头。【参考方案2】:WebClient
是一个流媒体客户端,在流结束之前设置内容长度有点困难。到那时,标题早已不复存在。如果您使用旧版,您可以重新使用您的单声道(Mono/Flux 可以重复使用,Java 流不能)并检查长度。
public void post()
Mono<String> mono = Mono.just("HELLO WORLDZ");
final String response = WebClient.create("http://httpbin.org")
.post()
.uri("/post")
.header(HttpHeaders.CONTENT_LENGTH,
mono.map(s -> String.valueOf(s.getBytes(StandardCharsets.UTF_8).length)).block())
.body(BodyInserters.fromPublisher(mono, String.class))
.retrieve()
.bodyToMono(String.class)
.block();
System.out.println(response);
我的一位同事(干得好,Max!)提出了更简洁的解决方案,我添加了一些包装代码以便对其进行测试:
Mono<String> my = Mono.just("HELLO WORLDZZ")
.flatMap(body -> WebClient.create("http://httpbin.org")
.post()
.uri("/post")
.header(HttpHeaders.CONTENT_LENGTH,
String.valueOf(body.getBytes(StandardCharsets.UTF_8).length))
.syncBody(body)
.retrieve()
.bodyToMono(String.class));
System.out.println(my.block());
【讨论】:
这仅适用于纯字符串主体,不适用于 POJO 到 JSON 之类的映射对象,还是这样? 自 2018 年以来有一个回复可以回答您的问题... 当您使用 WebClient.syncBody(myPojo)
的自动映射功能将 Pojo 作为 syncBody 传递时,即使 brebDev 的答案也无法解决这种情况,因为在这种情况下,我不知道序列化有多少个字符会,或者我可以吗?还是显式映射是唯一的出路?很抱歉我的第一个问题表述得很草率。
简单解决方案:使用 2.2 或更高版本。问题已修复:github.com/spring-projects/spring-framework/issues/21085 in 2019【参考方案3】:
如果您像我们一样应用 Sven 的 colleague(Max) 解决方案,您也可以将其调整为适用于您的身体是自定义 obj 但您必须对其进行序列化一次的情况:
String req = objectMapper.writeValueAsString(requestObject)
并将其传递给
webClient.syncBody(req)
请记住,在 SpringBoot 2.0.3.RELEASE 中,如果您将字符串作为请求传递给 webClient,它将作为 ContentType 标头 MediaType.TEXT_PLAIN 放置,这会使我们与其他服务的集成失败。我们通过像这样专门设置内容类型标题来解决这个问题:
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
【讨论】:
以上是关于缺少使用 WebClient 发送 POST 请求的 Content-Length 标头(SpringBoot 2.0.2.RELEASE)的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 WebClient 反应式 Web 客户端发送带有 zip 正文的 POST 请求
如何在 webclient 的 post 请求中发送 x-www-form-urlencoded? [复制]
如何向 Discord Webhooks C# 发送 POST 请求