Spring Web 客户端返回异常“bodyType=[responseObject] 不支持内容类型‘application/json’”

Vin*_*eng 8 java spring-boot spring-webclient

我使用Web客户端发布第三方API,第三方API返回响应如下:

\n
{\n    "RetailTransactionGenericResponse": {\n        "authID": 1146185,\n        "product": {\n            "amount": 20,\n            "balance": 0,\n            "status": "D",\n            "metafields": {\n                "metafield": [\n                    {\n                        "name": "serialNum",\n                        "value": 8095843490\n                    }\n                ]\n            }\n        },\n        "responseCode": 0,\n        "responseText": "Success",\n        "transactionID": "b0924cca-f2a9-477f-915b-9153e74ebce0"\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

所以在我的payload包中,它有RetailTransactionGenericResponseWrapper类,其中包含RetailTransactionGenericResponse类。

\n

RetailTransactionGenericResponse这是类的代码

\n
import com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.ToString;\n\n@Data\n@ToString\n@Builder\n@AllArgsConstructor(onConstructor_ = @JsonCreator)\n@JsonInclude(JsonInclude.Include.NON_NULL)\npublic class RetailTransactionGenericResponse {\n\n    @JsonProperty\n    private String authID;\n\n    @JsonProperty\n    private Product product;\n\n    @JsonProperty\n    private String responseCode;\n\n    @JsonProperty\n    private String responseText;\n\n    @JsonProperty\n    private String transactionID;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

上课Product

\n
import com.fasterxml.jackson.annotation.JsonInclude;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.databind.annotation.JsonDeserialize;\nimport com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.apache.commons.lang3.builder.ReflectionToStringBuilder;\nimport org.apache.commons.lang3.builder.ToStringStyle;\n\n@Data\n@Builder\n@AllArgsConstructor\n@NoArgsConstructor\n@JsonInclude(JsonInclude.Include.NON_NULL)\npublic class Product {\n\n    @JsonProperty\n    @JsonInclude(JsonInclude.Include.NON_NULL)\n    private double amount;\n\n    @JsonProperty\n    @JsonInclude(JsonInclude.Include.NON_NULL)\n    private String code;\n\n    @JsonProperty\n    @JsonInclude(JsonInclude.Include.NON_NULL)\n    private String upc;\n\n    // Response part\n\n    @JsonProperty\n    @JsonInclude(JsonInclude.Include.NON_NULL)\n    private MetaFields metafields;\n\n    @JsonProperty\n    @JsonInclude(JsonInclude.Include.NON_NULL)\n    private String balance;\n\n    @JsonProperty\n    @JsonInclude(JsonInclude.Include.NON_NULL)\n    private String status;\n\n    @Override\n    public String toString() {\n        return ReflectionToStringBuilder.toString(this, ToStringStyle.JSON_STYLE);\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

和对于MetaFields班级

\n
import com.fasterxml.jackson.annotation.JsonInclude;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Data\n@JsonInclude(JsonInclude.Include.NON_NULL)\npublic class MetaFields {\n\n    @JsonProperty\n    private List<MetaField> metafield;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

MetaField班级

\n
import com.fasterxml.jackson.annotation.JsonInclude;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\n@Data\n@JsonInclude(JsonInclude.Include.NON_NULL)\npublic class MetaField {\n\n    @JsonProperty\n    private String name;\n\n    @JsonProperty\n    private String value;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

所以在我的服务中,这是我尝试检索响应的方法

\n
RetailTransactionGenericResponseWrapper response =\n    webClient.post().uri(url).headers(headers -> headers.setBearerAuth(token)).accept(APPLICATION_JSON)\n        .body(Mono.just(retailTransactionGenericRequest), RetailTransactionGenericResponseWrapper.class)\n        .retrieve().bodyToMono(RetailTransactionGenericResponseWrapper.class).block();\n
Run Code Online (Sandbox Code Playgroud)\n

我做错了什么,它无法将 json 映射到对象?

\n

这是异常堆栈

\n
ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.reactive.function.client.WebClientResponseException: 200 OK from POST https://[third_party_url]; nested exception is org.springframework.web.reactive.function.UnsupportedMediaTypeException: Content type 'application/json' not supported for bodyType=com.xxx.xxx.payload.xxx.RetailTransactionGenericResponseWrapper] with root cause\norg.springframework.web.reactive.function.UnsupportedMediaTypeException: Content type 'application/json' not supported for bodyType=com.xxx.xxx.payload.xxx.RetailTransactionGenericResponseWrapper\n    at org.springframework.web.reactive.function.BodyExtractors.lambda$readWithMessageReaders$12(BodyExtractors.java:201)\n    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: \nError has been observed at the following site(s):\n    |_ checkpoint \xe2\x87\xa2 Body from POST https://[third_party_url] [DefaultClientResponse]\nStack trace:\n        at org.springframework.web.reactive.function.BodyExtractors.lambda$readWithMessageReaders$12(BodyExtractors.java:201)\n        at java.base/java.util.Optional.orElseGet(Optional.java:369)\n        at org.springframework.web.reactive.function.BodyExtractors.readWithMessageReaders(BodyExtractors.java:197)\n        at org.springframework.web.reactive.function.BodyExtractors.lambda$toMono$2(BodyExtractors.java:85)\n        at org.springframework.web.reactive.function.client.DefaultClientResponse.body(DefaultClientResponse.java:132)\n        at org.springframework.web.reactive.function.client.DefaultClientResponse.bodyToMono(DefaultClientResponse.java:147)\n        at org.springframework.web.reactive.function.client.DefaultWebClient$DefaultResponseSpec.lambda$bodyToMono$2(DefaultWebClient.java:541)\n        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:125)\n        at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:73)\n        at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:120)\n        at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79)\n        at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:199)\n        at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:199)\n        at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:199)\n        at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:82)\n        at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2346)\n        at reactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onSubscribeInner(MonoFlatMapMany.java:150)\n        at reactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onNext(MonoFlatMapMany.java:189)\n        at reactor.core.publisher.SerializedSubscriber.onNext(SerializedSubscriber.java:99)\n        at reactor.core.publisher.FluxRetryWhen$RetryWhenMainSubscriber.onNext(FluxRetryWhen.java:173)\n        at reactor.core.publisher.MonoCreate$DefaultMonoSink.success(MonoCreate.java:160)\n        at reactor.netty.http.client.HttpClientConnect$HttpIOHandlerObserver.onStateChange(HttpClientConnect.java:389)\n        at reactor.netty.ReactorNetty$CompositeConnectionObserver.onStateChange(ReactorNetty.java:612)\n        at reactor.netty.resources.DefaultPooledConnectionProvider$DisposableAcquire.onStateChange(DefaultPooledConnectionProvider.java:195)\n        at reactor.netty.resources.DefaultPooledConnectionProvider$PooledConnection.onStateChange(DefaultPooledConnectionProvider.java:466)\n        at reactor.netty.http.client.HttpClientOperations.onInboundNext(HttpClientOperations.java:613)\n        at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:94)\n        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)\n        at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:286)\n        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)\n        at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)\n        at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:324)\n        at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:311)\n        at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:432)\n        at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276)\n        at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)\n        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)\n        at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1533)\n        at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1282)\n        at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1329)\n        at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:508)\n        at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:447)\n        at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276)\n        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)\n        at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)\n        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n        at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)\n        at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)\n        at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:719)\n        at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:655)\n        at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:581)\n        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)\n        at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)\n        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)\n        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)\n        at java.base/java.lang.Thread.run(Thread.java:834)\n
Run Code Online (Sandbox Code Playgroud)\n

根据尼古拉的评论,我尝试遵循......

\n
RetailTransactionGenericResponseWrapper response =\n    webClient.post().uri(url).header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)\n        .headers(headers -> {\n            headers.setBearerAuth(token);\n        }).accept(APPLICATION_JSON)\n        .body(Mono.just(retailTransactionGenericRequest), RetailTransactionGenericResponseWrapper.class)\n        .retrieve().bodyToMono(RetailTransactionGenericResponseWrapper.class).block();\n
Run Code Online (Sandbox Code Playgroud)\n

像这样

\n
RetailTransactionGenericResponseWrapper response = webClient.post().uri(url).headers(headers -> {\n    headers.setBearerAuth(token);\n    headers.setContentType(APPLICATION_JSON);\n}).contentType(APPLICATION_JSON).accept(APPLICATION_JSON)\n    .body(Mono.just(retailTransactionGenericRequest), RetailTransactionGenericResponseWrapper.class).retrieve()\n    .bodyToMono(RetailTransactionGenericResponseWrapper.class).block();\n
Run Code Online (Sandbox Code Playgroud)\n

我仍然得到相同的异常堆栈

\n

Nik*_*nko 1

将以下标头添加到您的 WebClient 请求中

webClient
   //...
   .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
   //...
Run Code Online (Sandbox Code Playgroud)

如果没有这个,Jackson 的 JSON 编解码器就不会注册为正文提取器候选者。如果您不指定“Content-Type”标头,则默认情况下它将是“application/octet-stream”,Jackson 不支持该标头。

  • 不知何故仍然不起作用...我已经编辑了帖子 (3认同)