Spring reactive WebClient GET json response with Content-Type "text/plain;charset=UTF-8"
Posted
技术标签:
【中文标题】Spring reactive WebClient GET json response with Content-Type "text/plain;charset=UTF-8"【英文标题】: 【发布时间】:2020-07-30 03:33:39 【问题描述】:我在使用 Spring 5 响应式 WebClient 时遇到问题,当我请求一个端点,该端点返回格式正确的 json 响应,内容类型为“text/plain;charset=UTF-8”。 例外是
org.springframework.web.reactive.function.UnsupportedMediaTypeException:
Content type 'text/plain;charset=UTF-8' not supported for bodyType=MyDTOClass
这是我提出请求的方式:
webClient.get().uri(endpoint).retrieve().bodyToFlux(MyDTOClass.class)
编辑:标题已“正确”设置(接受、内容类型),我尝试了不同的内容类型(json、json + UTF8、纯文本、纯文本 + UTF8)组合,没有成功。我认为问题是.bodyToFlux(MyDTOClass.class)
不知道如何将“文本”翻译成MyDTOClass
对象。
如果我将请求更改为:
webClient.get().uri(endpoint).retrieve().bodyToFlux(String.class)
我可以读取字符串。
EDIT 2:下一句摘自 Spring 文档 (https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-codecs-jackson)
默认情况下
Jackson2Encoder
和Jackson2Decoder
都不支持String
类型的元素。相反,默认假设是 字符串或字符串序列表示序列化的 JSON 内容,以 由CharSequenceEncoder
呈现。如果你需要的是渲染 来自Flux<String>
的 JSON 数组,使用Flux#collectToList()
并编码Mono<List<String>>
.
我认为解决方案是定义一个新的解码器/读取器,以便将字符串转换为 MyDTOClass,但我不知道该怎么做。
【问题讨论】:
你可以试试this.webClient = WebClient.builder() .baseUrl(clientUrl) .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_UTF8_VALUE) .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE) .build();
这个问题与我的请求头无关,我已经在帖子中解释过了。
您是否尝试将响应映射为字符串并将其打印出来仅用于调试目的?只是为了确定会发生什么作为回应。还有一件事端点返回的是什么?就像端点返回的响应真的可以映射到您的 DTO 中?
您也可以在这里发布您的 DTO 课程吗?
是的,如果我使用 .bodyToFlux(String.class)
并且我的 DTO 与响应完全匹配,我可以读取字符串。我手动将响应提取到资源目录中名为 response.json
的文件中,然后我将网络客户端指向localhost/response.json 可以毫无问题地读取它,如果我将文件扩展名更改为response.txt
它会引发相同的异常。
【参考方案1】:
为 webclient 设置内容类型。
webClient.get()
.uri(endpoint)
.contentType(MediaType.APPLICATION_JSON_UTF8)
【讨论】:
它不能解决问题。我在帖子中添加了更多信息。【参考方案2】:如果有人需要,这里是解决方案:
这个答案 (https://***.com/a/57046640/13333357) 是关键。我们必须添加一个自定义解码器来指定反序列化响应的内容和方式。
但我们要记住这一点:类级别注解@JsonIgnoreProperties
默认设置为json映射器,对其他映射器没有影响。因此,如果您的 DTO 不匹配所有响应“json”属性,则反序列化将失败。
以下是如何配置 ObjectMapper 和 WebClient 以从文本响应中反序列化 json 对象:
...
WebClient.builder()
.baseUrl(url)
.exchangeStrategies(ExchangeStrategies.builder().codecs(configurer ->
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
configurer.customCodecs().decoder(new Jackson2JsonDecoder(mapper, MimeTypeUtils.parseMimeType(MediaType.TEXT_PLAIN_VALUE)));
).build())
.build();
...
干杯!
【讨论】:
【参考方案3】:根据上述 Charlie 的 this answer,您现在可以添加额外的“编解码器”而无需替换它们。
你也可以通过Jackson2ObjectMapperBuilder.json().build()
轻松构建Spring默认配置的ObjectMapper
这是一个重用内置 Jackson2JsonDecoder 中的 ObjectMapper 的示例
var webClient = webClientBuilder
.baseUrl(properties.getBaseUrl())
.codecs(configurer ->
// This API returns JSON with content type text/plain, so need to register a custom
// decoder to deserialize this response via Jackson
// Get existing decoder's ObjectMapper if available, or create new one
ObjectMapper objectMapper = configurer.getReaders().stream()
.filter(reader -> reader instanceof Jackson2JsonDecoder)
.map(reader -> (Jackson2JsonDecoder) reader)
.map(reader -> reader.getObjectMapper())
.findFirst()
.orElseGet(() -> Jackson2ObjectMapperBuilder.json().build());
Jackson2JsonDecoder decoder = new Jackson2JsonDecoder(objectMapper, MediaType.TEXT_PLAIN);
configurer.customCodecs().registerWithDefaultConfig(decoder);
)
.build();
【讨论】:
以上是关于Spring reactive WebClient GET json response with Content-Type "text/plain;charset=UTF-8"的主要内容,如果未能解决你的问题,请参考以下文章
使用 Spring Security OAuth 时 Spring Boot Reactive WebClient “serverWebExchange must be null”
Spring reactive WebClient GET json response with Content-Type "text/plain;charset=UTF-8"
Spring Webflux Webclient |内容类型标题设置问题