在接口服務開發中,我們經常用到Spring模闆類RestTemplate通路restful服務。但RestTemplate進行中文亂碼問題比較麻煩。以我們項目Spring版本4.1.3.RELEASE為例,RestTemplate預設構造方法初始化的StringHttpMessageConverter的預設字元集是 ISO-8859-1,是以導緻RestTemplate請求的響應内容會出現中文亂碼。處理方法可如下:import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
public class RestTemplateUtil {
public static RestTemplate getInstance() {
RestTemplate restTemplate = new RestTemplate();
// 設定編碼
restTemplate.getMessageConverters()
.set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
// 列印日志
restTemplate.setInterceptors(
Collections.singletonList(new LoggingClientHttpRequestInterceptor()));
return restTemplate;
}
}
class LoggingClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
private final static Logger LOGGER = LoggerFactory.getLogger(LoggingClientHttpRequestInterceptor.class);
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
traceRequest(request, body);
ClientHttpResponse response = execution.execute(request, body);
traceResponse(response);
return response;
}
private void traceRequest(HttpRequest request, byte[] body) throws IOException {
LOGGER.debug("======================request begin========================================");
LOGGER.debug("URI : {}", request.getURI());
LOGGER.debug("Method : {}", request.getMethod());
LOGGER.debug("Headers : {}", request.getHeaders());
LOGGER.debug("Request body: {}", new String(body, "UTF-8"));
LOGGER.debug("=====================request end===========================================");
}
private void traceResponse(ClientHttpResponse response) throws IOException {
//StringBuilder inputStringBuilder = new StringBuilder();
//try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getBody(), "UTF-8"))) {
//String line = bufferedReader.readLine();
//while (line != null) {
//inputStringBuilder.append(line);
//inputStringBuilder.append('\n');
//line = bufferedReader.readLine();
//}
//}
LOGGER.debug("=========================response begin=======================================");
LOGGER.debug("Status code : {}", response.getStatusCode());
LOGGER.debug("Status text : {}", response.getStatusText());
LOGGER.debug("Headers : {}", response.getHeaders());
// WARNING: comment out in production to improve performance
LOGGER.debug("Response body: {}", "WARNING: comment out in production to improve performance");
LOGGER.debug("====================response end==============================================");
}
}
如果其他Spring版本無法按如上方法解決,可運用類加載機制,從根源上解決問題,即重寫org.springframework.http.converter.StringHttpMessageConverter.class,将DEFAULT_CHARSET的值改為Charset.forName("UTF-8")。以spring版本4.1.3.RELEASE為例:
package org.springframework.http.converter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.util.StreamUtils;
*}),
* 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
* @since 3.0
*/
public class StringHttpMessageConverter extends AbstractHttpMessageConverter {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private final Charset defaultCharset;
private final List availableCharsets;
private boolean writeAcceptCharset = true;
public StringHttpMessageConverter() {
this(DEFAULT_CHARSET);
}
public StringHttpMessageConverter(Charset defaultCharset) {
super(new MediaType("text", "plain", defaultCharset), MediaType.ALL);
this.defaultCharset = defaultCharset;
this.availableCharsets = new ArrayList(Charset.availableCharsets().values());
}
public void setWriteAcceptCharset(boolean writeAcceptCharset) {
this.writeAcceptCharset = writeAcceptCharset;
}
@Override
public boolean supports(Class> clazz) {
return String.class.equals(clazz);
}
@Override
protected String readInternal(Class extends String> clazz, HttpInputMessage inputMessage) throws IOException {
Charset charset = getContentTypeCharset(inputMessage.getHeaders().getContentType());
return StreamUtils.copyToString(inputMessage.getBody(), charset);
}
@Override
protected Long getContentLength(String str, MediaType contentType) {
Charset charset = getContentTypeCharset(contentType);
try {
return (long) str.getBytes(charset.name()).length;
}
catch (UnsupportedEncodingException ex) {
// should not occur
throw new IllegalStateException(ex);
}
}
@Override
protected void writeInternal(String str, HttpOutputMessage outputMessage) throws IOException {
if (this.writeAcceptCharset) {
outputMessage.getHeaders().setAcceptCharset(getAcceptedCharsets());
}
Charset charset = getContentTypeCharset(outputMessage.getHeaders().getContentType());
StreamUtils.copy(str, charset, outputMessage.getBody());
}
protected List getAcceptedCharsets() {
return this.availableCharsets;
}
private Charset getContentTypeCharset(MediaType contentType) {
if (contentType != null && contentType.getCharSet() != null) {
return contentType.getCharSet();
}
else {
return this.defaultCharset;
}
}
}