如何使用 WebClient 反应式 Web 客户端发送带有 zip 正文的 POST 请求

Posted

技术标签:

【中文标题】如何使用 WebClient 反应式 Web 客户端发送带有 zip 正文的 POST 请求【英文标题】:How can I send POST request with zip body using WebClient reactive web client 【发布时间】:2019-06-26 12:39:55 【问题描述】:

如何使用 WebClient 响应式 Web 客户端发布带有 ZIP(压缩数据)正文的请求。

我在内存中创建了一个 zip (bytearrayoutputstream),并希望使用 WebClient 响应式 Web 客户端在 POST 请求正文中发送 zip 数据。 REST API 是一个 PdfReactor REST WebService。响应是二进制数据 (pdf)。

当我调试我的代码时,我收到以下错误: 池化连接观察到错误

org.springframework.web.reactive.function.UnsupportedMediaTypeException: Content type 'application/octet-stream' not supported for bodyType=java.io.ByteArrayInputStream
    at org.springframework.web.reactive.function.BodyInserters.unsupportedError(BodyInserters.java:297) ~[spring-webflux-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.web.reactive.function.BodyInserters.lambda$writeWithMessageWriters$9(BodyInserters.java:287) ~[spring-webflux-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at java.base/java.util.Optional.orElseGet(Optional.java:358) ~[na:na]
    at org.springframework.web.reactive.function.BodyInserters.writeWithMessageWriters(BodyInserters.java:287) ~[spring-webflux-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.web.reactive.function.BodyInserters.lambda$fromObject$1(BodyInserters.java:85) ~[spring-webflux-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.web.reactive.function.client.DefaultClientRequestBuilder$BodyInserterRequest.writeTo(DefaultClientRequestBuilder.java:257) ~[spring-webflux-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction.lambda$exchange$1(ExchangeFunctions.java:103) ~[spring-webflux-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.http.client.reactive.ReactorClientHttpConnector.lambda$connect$2(ReactorClientHttpConnector.java:110) ~[spring-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at reactor.netty.http.client.HttpClientConnect$HttpClientHandler.requestWithBody(HttpClientConnect.java:528) ~[reactor-netty-0.8.2.RELEASE.jar:0.8.2.RELEASE]
    at reactor.netty.http.client.HttpClientConnect$HttpObserver.lambda$onStateChange$0(HttpClientConnect.java:396) [reactor-netty-0.8.2.RELEASE.jar:0.8.2.RELEASE]
    at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:44) ~[reactor-core-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at reactor.core.publisher.MonoOnAssembly.subscribe(MonoOnAssembly.java:76) [reactor-core-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at reactor.netty.http.client.HttpClientConnect$HttpObserver.onStateChange(HttpClientConnect.java:397) [reactor-netty-0.8.2.RELEASE.jar:0.8.2.RELEASE]
    at reactor.netty.resources.PooledConnectionProvider$DisposableAcquire.onStateChange(PooledConnectionProvider.java:501) [reactor-netty-0.8.2.RELEASE.jar:0.8.2.RELEASE]
    at reactor.netty.resources.PooledConnectionProvider$DisposableAcquire.run(PooledConnectionProvider.java:531) [reactor-netty-0.8.2.RELEASE.jar:0.8.2.RELEASE]
    at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163) [netty-common-4.1.29.Final.jar:4.1.29.Final]
    at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404) [netty-common-4.1.29.Final.jar:4.1.29.Final]
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:446) [netty-transport-4.1.29.Final.jar:4.1.29.Final]
    at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884) [netty-common-4.1.29.Final.jar:4.1.29.Final]
    at java.base/java.lang.Thread.run(Thread.java:844) [na:na]
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
    |_  Mono.error ⇢ org.springframework.web.reactive.function.BodyInserters.lambda$writeWithMessageWriters$9(BodyInserters.java:287)
    |_  Mono.defer ⇢ reactor.netty.http.client.HttpClientConnect$HttpObserver.onStateChange(HttpClientConnect.java:396)
 configuration.json:
    
    "document": "test.html",
    "addBookmarks": true,
    "addLinks": true
    

 test.html:
    <html>
     <img src="img/image.png" />
     <div>
    Hallo, hier ist ein Testdokument von <span th:text="$userName" />.
     </div>
    </html>
map.put(template, tempBytesArray);
map.put(configuration, configByteArray);

        try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) 
            try (ZipOutputStream zos_ = new ZipOutputStream(bos)) 
                map.forEach((k, v) -> 
                    var zipentry = new ZipEntry(k);
                    try 
                        zos_.putNextEntry(zipentry);
                        zos_.write(v);
                        zos_.closeEntry();
                     catch (IOException e) 
                        e.printStackTrace();
                    
                );
               byte[] b = bos.toByteArray();
               BodyInserter<Object, ReactiveHttpOutputMessage>     bodyInserter = BodyInserters.fromObject(bos);
               byteArray = pdfReactorClient
                        .post()
                        .uri(pdfReactorUrl + PDF_REACTOR_URL_SUFFIX)
                        .accept(MediaType.APPLICATION_OCTET_STREAM)
                        .body(BodyInserters.fromObject(bos)
                        .exchange()
                        .flatMap(response -> response.bodyToMono (ByteArrayResource.class))
                        .map(ByteArrayResource::getByteArray)
                        .block();
                zos_.close();
             catch (Exception e) 
                // TODO: handle exception
            

如果我在命令行上执行以下 CURL,我会得到 pdf 的结果:

curl -X POST -H "Cache-Control: no-cache" -H "Content-Type: application/zip" --data-binary @test.gzip "http://localhost:8080/service/rest/convert.pdf" &gt; result.pdf.

同样应该翻译成 Java 反应式代码。 PDFReactor 的请求正文(Zip/资产包)是问题所在。请帮忙...

PDFReactor

【问题讨论】:

你复制粘贴的日志太大,比如有tomcat启动的信息不相关。您能否更具体地说明日志中的错误? 【参考方案1】:

PDF 的内容类型是 MediaType.APPLICATION_PDF

如下图改变内容类型

                    pdfReactorClient
                        .post()
                        .uri(pdfReactorUrl + PDF_REACTOR_URL_SUFFIX)
                        .accept(MediaType.APPLICATION_PDF)

【讨论】:

嗨,巴勃罗和巴拉特, 嗨 Pablo & Barath,请我将接受媒体类型更改为您的建议: .post() .uri(pdfReactorUrl + PDF_REACTOR_URL_SUFFIX) .accept(MediaType.APPLICATION_PDF) .body(BodyInserters.fromObject(bos )) 但它重现了理智的错误:bodyType=java.io.ByteArrayInputStream 不支持内容类型'application/octet-stream' 以下操作员观察到错误:|_ Mono.error ⇢ org.springframework.web.reactive.function.BodyInserters.lambda$writeWithMessageWriters$9(BodyInserters.java:287) | _ Mono.defer ⇢ reactor.netty.http.client.HttpClientConnect$HttpObserver.onStateChange(HttpClientConnect.java:396)

以上是关于如何使用 WebClient 反应式 Web 客户端发送带有 zip 正文的 POST 请求的主要内容,如果未能解决你的问题,请参考以下文章

反应式 WebClient 未发出响应

Spring WebClient 使用简介

Spring WebClient 使用简介

Spring WebClient 使用简介

如何使用 WebClient 使用响应式 Spring Rest API

反应式 Spring WebClient 调用