在接口服务开发中,我们经常用到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为例:
/** * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 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; /** * 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 * @since 3.0 */ public class StringHttpMessageConverter extends AbstractHttpMessageConverter<String> { public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); private final Charset defaultCharset; private final List<Charset> availableCharsets; private boolean writeAcceptCharset = true; /** * A default constructor that uses {@code "ISO-8859-1"} as the default charset. * @see #StringHttpMessageConverter(Charset) */ public StringHttpMessageConverter() { this(DEFAULT_CHARSET); } /** * A constructor accepting a default charset to use if the requested content * type does not specify one. */ public StringHttpMessageConverter(Charset defaultCharset) { super(new MediaType("text", "plain", defaultCharset), MediaType.ALL); this.defaultCharset = defaultCharset; this.availableCharsets = new ArrayList<Charset>(Charset.availableCharsets().values()); } /** * Indicates whether the {@code Accept-Charset} should be written to any outgoing request. * <p>Default is {@code true}. */ 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()); } /** * Return the list of supported {@link Charset}s. * <p>By default, returns {@link Charset#availableCharsets()}. * Can be overridden in subclasses. * @return the list of accepted charsets */ protected List<Charset> getAcceptedCharsets() { return this.availableCharsets; } private Charset getContentTypeCharset(MediaType contentType) { if (contentType != null && contentType.getCharSet() != null) { return contentType.getCharSet(); } else { return this.defaultCharset; } } }
Copyright ©2017-2024 uzen.zone
湘ICP备17013178号-3