RestTemplate 中文乱码问题

前言

在一次使用 RestTemplate 请求数据的时候,发送如果包含中文,返回的结果就是乱码的。

String result = restTemplate.postForObject(url, json.toJSONString(), String.class);

Response:

"SUCCESS:{\"address\":\"????\",\"name\":\"??\",\"age\":18}"

为什么会乱码呢?
看一下 RestTemplate 的构造方法:

/**
* Create a new instance of the {@link RestTemplate} using default settings.
 * Default {@link HttpMessageConverter}s are initialized.
 */
public RestTemplate() {
	this.messageConverters.add(new ByteArrayHttpMessageConverter());
	this.messageConverters.add(new StringHttpMessageConverter());
	this.messageConverters.add(new ResourceHttpMessageConverter());
	this.messageConverters.add(new SourceHttpMessageConverter<Source>());
	this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());

	if (romePresent) {
		this.messageConverters.add(new AtomFeedHttpMessageConverter());
		this.messageConverters.add(new RssChannelHttpMessageConverter());
	}

	if (jackson2XmlPresent) {
		this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
	}
	else if (jaxb2Present) {
		this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
	}

	if (jackson2Present) {
		this.messageConverters.add(new MappingJackson2HttpMessageConverter());
	}
	else if (gsonPresent) {
		this.messageConverters.add(new GsonHttpMessageConverter());
	}
}

可以看到起添加了 StringHttpMessageConverter
在接收数据之后,RestTemplate 根据你要传输的接收数据类型来判断使用哪一个 HttpMessageConverter,我们看一下 postForObject

@Override
public <T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)
		throws RestClientException {

	RequestCallback requestCallback = httpEntityCallback(request, responseType);
	HttpMessageConverterExtractor<T> responseExtractor =
			new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
	return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
}

getMessageConverters 这个方法会获取 RestTemplate 的所有 HttpMessageConverter
看一下 execute 方法:

@Override
public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback,
		ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException {

	URI expanded = getUriTemplateHandler().expand(url, uriVariables);
	return doExecute(expanded, method, requestCallback, responseExtractor);
}

继续跟进:
doExecute

protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,
		ResponseExtractor<T> responseExtractor) throws RestClientException {

	Assert.notNull(url, "'url' must not be null");
	Assert.notNull(method, "'method' must not be null");
	ClientHttpResponse response = null;
	try {
		ClientHttpRequest request = createRequest(url, method);
		if (requestCallback != null) {
			requestCallback.doWithRequest(request);
		}
		response = request.execute();
		handleResponse(url, method, response);
		if (responseExtractor != null) {
			return responseExtractor.extractData(response);
		}
		else {
			return null;
		}
	}
	catch (IOException ex) {
		String resource = url.toString();
		String query = url.getRawQuery();
		resource = (query != null ? resource.substring(0, resource.indexOf(query) - 1) : resource);
		throw new ResourceAccessException("I/O error on " + method.name() +
				" request for \"" + resource + "\": " + ex.getMessage(), ex);
	}
	finally {
		if (response != null) {
			response.close();
		}
	}
}

看这行代码:

return responseExtractor.extractData(response);

实现类 HttpMessageConverterExtractor

@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);

	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);
			}
		}
	}

	throw new RestClientException("Could not extract response: no suitable HttpMessageConverter found " +
			"for response type [" + this.responseType + "] and content type [" + contentType + "]");
}

可以看到起会遍历我们的 HttpMessageConverter,并且判断其是否能读取我们配置的类型。
我们写的要返回 String.class 类型,你可以打断点 debug 下,其使用了StringHttpMessageConverter,点一去看一下:

public static final Charset DEFAULT_CHARSET = Charset.forName("ISO-8859-1");

其默认编码是 ISO-8859-1,所以就会出现乱码。

解决办法:

@Bean
public  RestTemplate restTemplate(String charset) {
    RestTemplate restTemplate = new RestTemplate();
    List<HttpMessageConverter<?>> list = restTemplate.getMessageConverters();
    for (HttpMessageConverter<?> httpMessageConverter : list) {
        if(httpMessageConverter instanceof StringHttpMessageConverter) {
            ((StringHttpMessageConverter) httpMessageConverter).setDefaultCharset(Charset.forName(charset));
            break;
        }
    }
    return restTemplate;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值