一、使用

  同前三节:ClientGetGoodsByGoodsIdResponse response = restTemplate.postForObject(svcUrl, request, Response.class);

二、问题汇总

1、no suitable HttpMessageConverter found for request type异常

这个问题通常会出现在postForObject中传入对象进行调用的时候。

分析RestTemplate源码,在HttpEntityRequestCallback类的doWithRequest方法中,如果messageConverters(这个字段后面会继续提及)列表字段循环处理的过程中没有满足return跳出的逻辑(也就是没有匹配的HttpMessageConverter),则抛出上述异常:

@Override
@SuppressWarnings("unchecked")
public void doWithRequest(ClientHttpRequest httpRequest) throws IOException {
super.doWithRequest(httpRequest);
Object requestBody = this.requestEntity.getBody();
if (requestBody == null) {
HttpHeaders httpHeaders = httpRequest.getHeaders();
HttpHeaders requestHeaders = this.requestEntity.getHeaders();
if (!requestHeaders.isEmpty()) {
for (Map.Entry<String, List<String>> entry : requestHeaders.entrySet()) {
httpHeaders.put(entry.getKey(), new LinkedList<>(entry.getValue()));
}
}
if (httpHeaders.getContentLength() < 0) {
httpHeaders.setContentLength(0L);
}
}
else {
Class<?> requestBodyClass = requestBody.getClass();
Type requestBodyType = (this.requestEntity instanceof RequestEntity ?
((RequestEntity<?>)this.requestEntity).getType() : requestBodyClass);
HttpHeaders httpHeaders = httpRequest.getHeaders();
HttpHeaders requestHeaders = this.requestEntity.getHeaders();
MediaType requestContentType = requestHeaders.getContentType();
for (HttpMessageConverter<?> messageConverter : getMessageConverters()) {
if (messageConverter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter<Object> genericConverter =
(GenericHttpMessageConverter<Object>) messageConverter;
if (genericConverter.canWrite(requestBodyType, requestBodyClass, requestContentType)) {
if (!requestHeaders.isEmpty()) {
for (Map.Entry<String, List<String>> entry : requestHeaders.entrySet()) {
httpHeaders.put(entry.getKey(), new LinkedList<>(entry.getValue()));
}
}
if (logger.isDebugEnabled()) {
if (requestContentType != null) {
logger.debug("Writing [" + requestBody + "] as \"" + requestContentType +
"\" using [" + messageConverter + "]");
}
else {
logger.debug("Writing [" + requestBody + "] using [" + messageConverter + "]");
} }
genericConverter.write(requestBody, requestBodyType, requestContentType, httpRequest);
return;
}
}
else if (messageConverter.canWrite(requestBodyClass, requestContentType)) {
if (!requestHeaders.isEmpty()) {
for (Map.Entry<String, List<String>> entry : requestHeaders.entrySet()) {
httpHeaders.put(entry.getKey(), new LinkedList<>(entry.getValue()));
}
}
if (logger.isDebugEnabled()) {
if (requestContentType != null) {
logger.debug("Writing [" + requestBody + "] as \"" + requestContentType +
"\" using [" + messageConverter + "]");
}
else {
logger.debug("Writing [" + requestBody + "] using [" + messageConverter + "]");
} }
((HttpMessageConverter<Object>) messageConverter).write(
requestBody, requestContentType, httpRequest);
return;
}
}
String message = "Could not write request: no suitable HttpMessageConverter found for request type [" +
requestBodyClass.getName() + "]";
if (requestContentType != null) {
message += " and content type [" + requestContentType + "]";
}
throw new RestClientException(message);
}
} HttpEntityRequestCallback.doWithRequest

最简单的解决方案是,可以通过包装http请求头,并将请求对象序列化成字符串的形式传参,参考示例代码如下:

    /*
* Post请求调用
* */
public static String postForObject(RestTemplate restTemplate, String url, Object params) {
HttpHeaders headers = new HttpHeaders();
MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
headers.setContentType(type);
headers.add("Accept", MediaType.APPLICATION_JSON.toString()); String json = JSON.toJSONString(params); HttpEntity<String> formEntity = new HttpEntity<String>(json, headers); String result = restTemplate.postForObject(url, formEntity, String.class); return result;
}

如果我们还想直接返回对象,直接反序列化返回的字符串即可:

    /*
* Post请求调用
* */
public static <T> T postForObject(RestTemplate restTemplate, String url, Object params, Class<T> clazz) {
T response = null; String respStr = postForObject(restTemplate, url, params); response = JSON.parseObject(respStr, clazz); return response;
}

其中,序列化和反序列化工具比较多,常用的比如fastjson、jackson和gson。

2、no suitable HttpMessageConverter found for response type异常

和发起请求发生异常一样,处理应答的时候也会有问题。

StackOverflow上有人问过相同的问题,根本原因是HTTP消息转换器HttpMessageConverter缺少MIME Type,也就是说HTTP在把输出结果传送到客户端的时候,客户端必须启动适当的应用程序来处理这个输出文档,这可以通过多种MIME(多功能网际邮件扩充协议)Type来完成。

对于服务端应答,很多HttpMessageConverter默认支持的媒体类型(MIMEType)都不同。StringHttpMessageConverter默认支持的则是MediaType.TEXT_PLAIN,SourceHttpMessageConverter默认支持的则是MediaType.TEXT_XML,FormHttpMessageConverter默认支持的是MediaType.APPLICATION_FORM_URLENCODED和MediaType.MULTIPART_FORM_DATA,在REST服务中,我们用到的最多的还是MappingJackson2HttpMessageConverter,这是一个比较通用的转化器(继承自GenericHttpMessageConverter接口),根据分析,它默认支持的MIMEType为MediaType.APPLICATION_JSON:

    /**
* Construct a new {@link MappingJackson2HttpMessageConverter} with a custom {@link ObjectMapper}.
* You can use {@link Jackson2ObjectMapperBuilder} to build it easily.
* @see Jackson2ObjectMapperBuilder#json()
*/
public MappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
super(objectMapper, MediaType.APPLICATION_JSON, new MediaType("application", "*+json"));
}

但是有些应用接口默认的应答MIMEType不是application/json,比如我们调用一个外部天气预报接口,如果使用RestTemplate的默认配置,直接返回一个字符串应答是没有问题的:

String url = "http://wthrcdn.etouch.cn/weather_mini?city=上海";
String result = restTemplate.getForObject(url, String.class);
ClientWeatherResultVO vo = SerializeUtil.DeSerialize(result, ClientWeatherResultVO.class);

但是,如果我们想直接返回一个实体对象:

String url = "http://wthrcdn.etouch.cn/weather_mini?city=上海";

ClientWeatherResultVO weatherResultVO = restTemplate.getForObject(url, ClientWeatherResultVO.class);

则直接报异常:
Could not extract response: no suitable HttpMessageConverter found for response type [class ]
and content type [application/octet-stream]

很多人碰到过这个问题,首次碰到估计大多都比较懵吧,很多接口都是json或者xml或者plain text格式返回的,什么是application/octet-stream?

查看RestTemplate源代码,一路跟踪下去会发现HttpMessageConverterExtractor类的extractData方法有个解析应答及反序列化逻辑,如果不成功,抛出的异常信息和上述一致:

@Override
@SuppressWarnings({"unchecked", "rawtypes", "resource"})
public T extractData(ClientHttpResponse response) throws IOException {
MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response);
if (!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) {
return null;
}
MediaType contentType = getContentType(responseWrapper); try {
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
if (messageConverter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter<?> genericMessageConverter =
(GenericHttpMessageConverter<?>) messageConverter;
if (genericMessageConverter.canRead(this.responseType, null, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Reading [" + this.responseType + "] as \"" +
contentType + "\" using [" + messageConverter + "]");
}
return (T) genericMessageConverter.read(this.responseType, null, responseWrapper);
}
}
if (this.responseClass != null) {
if (messageConverter.canRead(this.responseClass, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Reading [" + this.responseClass.getName() + "] as \"" +
contentType + "\" using [" + messageConverter + "]");
}
return (T) messageConverter.read((Class) this.responseClass, responseWrapper);
}
}
}
}
catch (IOException | HttpMessageNotReadableException ex) {
throw new RestClientException("Error while extracting response for type [" +
this.responseType + "] and content type [" + contentType + "]", ex);
} throw new RestClientException("Could not extract response: no suitable HttpMessageConverter found " +
"for response type [" + this.responseType + "] and content type [" + contentType + "]");
} HttpMessageConverterExtractor.extractData

StackOverflow上的解决的示例代码可以接受,但是并不准确,常见的MIMEType都应该加进去,贴一下我认为正确的代码:

package com.power.demo.restclient.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.http.MediaType;
import org.springframework.http.converter.*;
import org.springframework.http.converter.cbor.MappingJackson2CborHttpMessageConverter;
import org.springframework.http.converter.feed.AtomFeedHttpMessageConverter;
import org.springframework.http.converter.feed.RssChannelHttpMessageConverter;
import org.springframework.http.converter.json.GsonHttpMessageConverter;
import org.springframework.http.converter.json.JsonbHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.smile.MappingJackson2SmileHttpMessageConverter;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.web.client.RestTemplate; import java.util.Arrays;
import java.util.List; @Component
public class RestTemplateConfig { private static final boolean romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", RestTemplate
.class.getClassLoader());
private static final boolean jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", RestTemplate.class.getClassLoader());
private static final boolean jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", RestTemplate.class.getClassLoader()) && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", RestTemplate.class.getClassLoader());
private static final boolean jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", RestTemplate.class.getClassLoader());
private static final boolean jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", RestTemplate.class.getClassLoader());
private static final boolean jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", RestTemplate.class.getClassLoader());
private static final boolean gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", RestTemplate.class.getClassLoader());
private static final boolean jsonbPresent = ClassUtils.isPresent("javax.json.bind.Jsonb", RestTemplate.class.getClassLoader()); // 启动的时候要注意,由于我们在服务中注入了RestTemplate,所以启动的时候需要实例化该类的一个实例
@Autowired
private RestTemplateBuilder builder; @Autowired
private ObjectMapper objectMapper; // 使用RestTemplateBuilder来实例化RestTemplate对象,spring默认已经注入了RestTemplateBuilder实例
@Bean
public RestTemplate restTemplate() { RestTemplate restTemplate = builder.build(); List<HttpMessageConverter<?>> messageConverters = Lists.newArrayList();
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(objectMapper); //不加会出现异常
//Could not extract response: no suitable HttpMessageConverter found for response type [class ] MediaType[] mediaTypes = new MediaType[]{
MediaType.APPLICATION_JSON,
MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_JSON_UTF8,
MediaType.TEXT_HTML,
MediaType.TEXT_PLAIN,
MediaType.TEXT_XML,
MediaType.APPLICATION_STREAM_JSON,
MediaType.APPLICATION_ATOM_XML,
MediaType.APPLICATION_FORM_URLENCODED,
MediaType.APPLICATION_PDF,
}; converter.setSupportedMediaTypes(Arrays.asList(mediaTypes)); //messageConverters.add(converter);
if (jackson2Present) {
messageConverters.add(converter);
} else if (gsonPresent) {
messageConverters.add(new GsonHttpMessageConverter());
} else if (jsonbPresent) {
messageConverters.add(new JsonbHttpMessageConverter());
} messageConverters.add(new FormHttpMessageConverter()); messageConverters.add(new ByteArrayHttpMessageConverter());
messageConverters.add(new StringHttpMessageConverter());
messageConverters.add(new ResourceHttpMessageConverter(false));
messageConverters.add(new SourceHttpMessageConverter());
messageConverters.add(new AllEncompassingFormHttpMessageConverter());
if (romePresent) {
messageConverters.add(new AtomFeedHttpMessageConverter());
messageConverters.add(new RssChannelHttpMessageConverter());
} if (jackson2XmlPresent) {
messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
} else if (jaxb2Present) {
messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
} if (jackson2SmilePresent) {
messageConverters.add(new MappingJackson2SmileHttpMessageConverter());
} if (jackson2CborPresent) {
messageConverters.add(new MappingJackson2CborHttpMessageConverter());
} restTemplate.setMessageConverters(messageConverters); return restTemplate;
} } RestTemplateConfig

看到上面的代码,再对比一下RestTemplate内部实现,就知道我参考了RestTemplate的源码,有洁癖的人可能会说这一坨代码有点啰嗦,上面那一堆static final的变量和messageConverters填充数据方法,暴露了RestTemplate的实现,如果RestTemplate修改了,这里也要改,非常不友好,而且看上去一点也不OO。

经过分析,RestTemplateBuilder.build()构造了RestTemplate对象,只要将内部MappingJackson2HttpMessageConverter修改一下支持的MediaType即可,RestTemplate的messageConverters字段虽然是private final的,我们依然可以通过反射修改之,改进后的代码如下:

package com.power.demo.restclient.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate; import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors; @Component
public class RestTemplateConfig { // 启动的时候要注意,由于我们在服务中注入了RestTemplate,所以启动的时候需要实例化该类的一个实例
@Autowired
private RestTemplateBuilder builder; @Autowired
private ObjectMapper objectMapper; // 使用RestTemplateBuilder来实例化RestTemplate对象,spring默认已经注入了RestTemplateBuilder实例
@Bean
public RestTemplate restTemplate() { RestTemplate restTemplate = builder.build(); List<HttpMessageConverter<?>> messageConverters = Lists.newArrayList();
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(objectMapper); //不加可能会出现异常
//Could not extract response: no suitable HttpMessageConverter found for response type [class ] MediaType[] mediaTypes = new MediaType[]{
MediaType.APPLICATION_JSON,
MediaType.APPLICATION_OCTET_STREAM, MediaType.TEXT_HTML,
MediaType.TEXT_PLAIN,
MediaType.TEXT_XML,
MediaType.APPLICATION_STREAM_JSON,
MediaType.APPLICATION_ATOM_XML,
MediaType.APPLICATION_FORM_URLENCODED,
MediaType.APPLICATION_JSON_UTF8,
MediaType.APPLICATION_PDF,
}; converter.setSupportedMediaTypes(Arrays.asList(mediaTypes)); try {
//通过反射设置MessageConverters
Field field = restTemplate.getClass().getDeclaredField("messageConverters"); field.setAccessible(true); List<HttpMessageConverter<?>> orgConverterList = (List<HttpMessageConverter<?>>) field.get(restTemplate); Optional<HttpMessageConverter<?>> opConverter = orgConverterList.stream()
.filter(x -> x.getClass().getName().equalsIgnoreCase(MappingJackson2HttpMessageConverter.class
.getName()))
.findFirst(); if (opConverter.isPresent() == false) {
return restTemplate;
} messageConverters.add(converter);//添加MappingJackson2HttpMessageConverter //添加原有的剩余的HttpMessageConverter
List<HttpMessageConverter<?>> leftConverters = orgConverterList.stream()
.filter(x -> x.getClass().getName().equalsIgnoreCase(MappingJackson2HttpMessageConverter.class
.getName()) == false)
.collect(Collectors.toList()); messageConverters.addAll(leftConverters); System.out.println(String.format("【HttpMessageConverter】原有数量:%s,重新构造后数量:%s"
, orgConverterList.size(), messageConverters.size())); } catch (Exception e) {
e.printStackTrace();
} restTemplate.setMessageConverters(messageConverters); return restTemplate;
} } RestTemplateConfig

除了一个messageConverters字段,看上去我们不再关心RestTemplate那些外部依赖包和内部构造过程,果然干净简洁好维护了很多。

3、乱码问题

这个也是一个非常经典的问题。解决方案非常简单,找到HttpMessageConverter,看看默认支持的Charset。AbstractJackson2HttpMessageConverter是很多HttpMessageConverter的基类,默认编码为UTF-8:

public abstract class AbstractJackson2HttpMessageConverter extends AbstractGenericHttpMessageConverter<Object> {

    public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;

}

而StringHttpMessageConverter比较特殊,有人反馈过发生乱码问题由它默认支持的编码ISO-8859-1引起:

/**
* Implementation of {@link HttpMessageConverter} that can read and write strings.
*
* <p>By default, this converter supports all media types ({@code */*}),
* and writes with a {@code Content-Type} of {@code text/plain}. This can be overridden
* by setting the {@link #setSupportedMediaTypes supportedMediaTypes} property.
*
* @author Arjen Poutsma
* @author Juergen Hoeller
* @since 3.0
*/
public class StringHttpMessageConverter extends AbstractHttpMessageConverter<String> { public static final Charset DEFAULT_CHARSET = StandardCharsets.ISO_8859_1; /**
* A default constructor that uses {@code "ISO-8859-1"} as the default charset.
* @see #StringHttpMessageConverter(Charset)
*/
public StringHttpMessageConverter() {
this(DEFAULT_CHARSET);
} }

如果在使用过程中发生乱码,我们可以通过方法设置HttpMessageConverter支持的编码,常用的有UTF-8、GBK等。

4、反序列化异常

这是开发过程中容易碰到的又一个问题。因为Java的开源框架和工具类非常之多,而且版本更迭频繁,所以经常发生一些意想不到的坑。

以joda time为例,joda time是流行的java时间和日期框架,但是如果你的接口对外暴露joda time的类型,比如DateTime,那么接口调用方(同构和异构系统)可能会碰到序列化难题,反序列化时甚至直接抛出如下异常:

org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class org.joda.time.Chronology]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.joda.time.Chronology` (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
 at [Source: (PushbackInputStream);

我在前厂就碰到过,可以参考这里,后来为了调用方便,改回直接暴露Java的Date类型。

当然解决的方案不止这一种,可以使用jackson支持自定义类的序列化和反序列化的方式。在精度要求不是很高的系统里,实现简单的DateTime自定义序列化:

package com.power.demo.util;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter; import java.io.IOException; /**
* 在默认情况下,jackson会将joda time序列化为较为复杂的形式,不利于阅读,并且对象较大。
* <p>
* JodaTime 序列化的时候可以将datetime序列化为字符串,更容易读
**/
public class DateTimeSerializer extends JsonSerializer<DateTime> { private static DateTimeFormatter dateFormatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"); @Override
public void serialize(DateTime value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeString(value.toString(dateFormatter));
}
}

以及DateTime反序列化:

package com.power.demo.util;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter; import java.io.IOException; /**
* JodaTime 反序列化将字符串转化为datetime
**/
public class DatetimeDeserializer extends JsonDeserializer<DateTime> { private static DateTimeFormatter dateFormatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"); @Override
public DateTime deserialize(JsonParser jp, DeserializationContext context) throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
String s = node.asText();
DateTime parse = DateTime.parse(s, dateFormatter);
return parse;
}
}

最后可以在RestTemplateConfig类中对常见调用问题进行汇总处理,可以参考如下:

package com.power.demo.restclient.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.google.common.collect.Lists;
import com.power.demo.util.DateTimeSerializer;
import com.power.demo.util.DatetimeDeserializer;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate; import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors; @Component
public class RestTemplateConfig { // 启动的时候要注意,由于我们在服务中注入了RestTemplate,所以启动的时候需要实例化该类的一个实例
@Autowired
private RestTemplateBuilder builder; @Autowired
private ObjectMapper objectMapper; // 使用RestTemplateBuilder来实例化RestTemplate对象,spring默认已经注入了RestTemplateBuilder实例
@Bean
public RestTemplate restTemplate() { RestTemplate restTemplate = builder.build(); //注册model,用于实现jackson joda time序列化和反序列化
SimpleModule module = new SimpleModule();
module.addSerializer(DateTime.class, new DateTimeSerializer());
module.addDeserializer(DateTime.class, new DatetimeDeserializer());
objectMapper.registerModule(module); List<HttpMessageConverter<?>> messageConverters = Lists.newArrayList();
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(objectMapper); //不加会出现异常
//Could not extract response: no suitable HttpMessageConverter found for response type [class ]
MediaType[] mediaTypes = new MediaType[]{
MediaType.APPLICATION_JSON,
MediaType.APPLICATION_OCTET_STREAM, MediaType.TEXT_HTML,
MediaType.TEXT_PLAIN,
MediaType.TEXT_XML,
MediaType.APPLICATION_STREAM_JSON,
MediaType.APPLICATION_ATOM_XML,
MediaType.APPLICATION_FORM_URLENCODED,
MediaType.APPLICATION_JSON_UTF8,
MediaType.APPLICATION_PDF,
}; converter.setSupportedMediaTypes(Arrays.asList(mediaTypes)); try {
//通过反射设置MessageConverters
Field field = restTemplate.getClass().getDeclaredField("messageConverters"); field.setAccessible(true); List<HttpMessageConverter<?>> orgConverterList = (List<HttpMessageConverter<?>>) field.get(restTemplate); Optional<HttpMessageConverter<?>> opConverter = orgConverterList.stream()
.filter(x -> x.getClass().getName().equalsIgnoreCase(MappingJackson2HttpMessageConverter.class
.getName()))
.findFirst(); if (opConverter.isPresent() == false) {
return restTemplate;
} messageConverters.add(converter);//添加MappingJackson2HttpMessageConverter //添加原有的剩余的HttpMessageConverter
List<HttpMessageConverter<?>> leftConverters = orgConverterList.stream()
.filter(x -> x.getClass().getName().equalsIgnoreCase(MappingJackson2HttpMessageConverter.class
.getName()) == false)
.collect(Collectors.toList()); messageConverters.addAll(leftConverters); System.out.println(String.format("【HttpMessageConverter】原有数量:%s,重新构造后数量:%s"
, orgConverterList.size(), messageConverters.size())); } catch (Exception e) {
e.printStackTrace();
} restTemplate.setMessageConverters(messageConverters); return restTemplate;
} }

目前良好地解决了RestTemplate常见调用问题,而且不需要你写RestTemplate帮助工具类了。

原文地址:https://www.cnblogs.com/jeffwongishandsome/archive/2018/05/17/8995562.html

004-RestTemplate 使用常见问题的更多相关文章

  1. Spring Boot使用RestTemplate消费REST服务的几个问题记录

    我们可以通过Spring Boot快速开发REST接口,同时也可能需要在实现接口的过程中,通过Spring Boot调用内外部REST接口完成业务逻辑. 在Spring Boot中,调用REST Ap ...

  2. RestTemplate 使用中的几个问题

    Spring Boot使用RestTemplate消费REST服务的几个问题记录 我们可以通过Spring Boot快速开发REST接口,同时也可能需要在实现接口的过程中,通过Spring Boot调 ...

  3. HttpClient的替代者 - RestTemplate

    需要的包 ,除了Spring的基础包外还用到json的包,这里的数据传输使用json格式 客户端和服务端都用到一下的包 <!-- Spring --> <dependency> ...

  4. RestTemplate发送请求并携带header信息

    1.使用restTemplate的postForObject方法 注:目前没有发现发送携带header信息的getForObject方法. HttpHeaders headers = new Http ...

  5. C++常见笔试面试要点以及常见问题

    1. C++常见笔试面试要点: C++语言相关: (1) 虚函数(多态)的内部实现 (2) 智能指针用过哪些?shared_ptr和unique_ptr用的时候需要注意什么?shared_ptr的实现 ...

  6. SQL Server常见问题介绍及快速解决建议

    前言 本文旨在帮助SQL Server数据库的使用人员了解常见的问题,及快速解决这些问题.这些问题是数据库的常规管理问题,对于很多对数据库没有深入了解的朋友提供一个大概的常见问题框架. 下面一些问题是 ...

  7. 【腾讯优测干货分享】如何降低App的待机内存(二)——规范测试流程及常见问题

    本文来自于腾讯优测公众号(wxutest),未经作者同意,请勿转载,原文地址:https://mp.weixin.qq.com/s/806TiugiSJvFI7fH6eVA5w 作者:腾讯TMQ专项测 ...

  8. mysql数据库开发常见问题及优化

    mysql 数据库是被广泛应用的关系型数据库,其体积小.支持多处理器.开源并免费的特性使其在 Internet 中小型网站中的使用率尤其高.在使用 mysql 的过程中不规范的 SQL 编写.非最优的 ...

  9. 《PDF.NE数据框架常见问题及解决方案-初》

    <PDF.NE数据框架常见问题及解决方案-初> 1.新增数据库后,获取标识列的值: 解决方案:    PDF.NET数据框架,已经为我们考略了很多,因为用PDF.NET进行数据的添加操作时 ...

  10. 【Spring-web】RestTemplate源码学习——梳理内部实现过程

    2016-12-28 by 安静的下雪天  http://www.cnblogs.com/quiet-snowy-day/p/6228198.html  提示:使用手机浏览时请注意,图多费流量. 本篇 ...

随机推荐

  1. [Algorithm] Maximum Flow

    Ref MIT: lecture-13-incremental-improvement-max-flow-min-cut/ Ford Fulkerson algorithm for finding m ...

  2. Qt编写软件运行时间记录(开源)

    在早期开发的软件中,尤其是初学者入门者写的软件,软件运行久了,难免遇到意外崩溃的时候,可是大部分的运行设备可能在现场客户那,需要记住每一次从软件启动后到软件意外关闭前的运行时间,需要记录的信息包括:编 ...

  3. MySQL 出现You can't specify target table for update in FROM clause错误解决方法

    MySQL出现You can’t specify target table for update in FROM clause 这个错误的意思是不能在同一个sql语句中,先select同一个表的某些值 ...

  4. Pthon Matplotlib 画图

    一.普通绘图 import matplotlib.pyplot as plt import numpy as np # 绘制普通图像 x = np.linspace(-1, 1, 50) y1 = 2 ...

  5. 高斯混合模型 GMM

    本文将涉及到用 EM 算法来求解 GMM 模型,文中会涉及几个统计学的概念,这里先罗列出来: 方差:用来描述数据的离散或波动程度. \[var(X) =  \frac{\sum_{i=1}^N( X_ ...

  6. Zabbix使用grafana展示图形

    系统环境查看 官网下载grafana wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.0.1-1. ...

  7. 泡泡一分钟:Towards real-time unsupervised monocular depth estimation on CPU

    Towards real-time unsupervised monocular depth estimation on CPU Matteo Poggi , Filippo Aleotti , Fa ...

  8. MySQL 安装 用户管理 常用命令

    MySQL目录 数据库概览   数据库介绍 Why Choose MySQL MySQL的前世今生 MySQL的安装   Windows安装MySQL5.721 installer版 Windows安 ...

  9. 托布利兹变换 toeplitz 变换

    托布利兹变换 toeplitz 变换 算术平均变换 '''An->C=>(A1+A2+A3+...+An)/n->C,K_ni=1/n=>+[AiK_ni->C (Yn- ...

  10. mysql与redis在各种情况下性能对比

    数据表结构 CREATE TABLE `jx_goods_test` ( `id` int(11) NOT NULL AUTO_INCREMENT, `goods_name` varchar(100) ...