如何使用 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 请求的主要内容,如果未能解决你的问题,请参考以下文章