• Spring MVC @ResponseBody响应中文乱码


    问题:
    在前端通过get请求服务端返回String类型的服务时,会出现中文乱码问题

    原因:
    由于spring默认对String类型的返回的编码采用的是 StringHttpMessageConverter
    >>> spring mvc的一个bug,spring MVC有一系列HttpMessageConverter去处理用@ResponseBody注解的返回值,如返回list则使用MappingJacksonHttpMessageConverter,返回string,则使用StringHttpMessageConverter,这个convert使用的是字符集是iso-8859-1,而且是final的:
    public static final Charset DEFAULT_CHARSET = Charset.forName("ISO-8859-1");


    解决办法:
    方案一:
    对于需要返回字符串的方法添加注解,如下:只针对单个方法生效,不全局生效

    @RequestMapping(value = "/getUsers", produces = "application/json; charset=utf-8")
    public String getAllUser()throws JsonGenerationException, JsonMappingException, IOException{
      List < User > users = userService.getAll();
      ObjectMapper om = new ObjectMapper();
      System.out.println(om.writeValueAsString(users));
      DataGrid dg = new DataGrid();
      dg.setData(users);
      return om.writeValueAsString(dg);
    }

    方案二:

    在spring-servlet.xml中加入:

    <mvc:annotation-driven>
    <mvc:message-converters register-defaults="true">
    <bean class="org.springframework.http.converter.StringHttpMessageConverter">
    <property name="supportedMediaTypes" value = "text/plain;charset=UTF-8" />
    </bean>
    </mvc:message-converters>
    </mvc:annotation-driven>

    方案三:
    重写一个MessageConverter,然后注册到AnnotationMethodHandlerAdapter

    package com.h5.common.converter;
    
    import org.springframework.http.HttpInputMessage;
    import org.springframework.http.HttpOutputMessage;
    import org.springframework.http.MediaType;
    import org.springframework.http.converter.AbstractHttpMessageConverter;
    import org.springframework.util.StreamUtils;
    
    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    import java.nio.charset.Charset;
    import java.util.ArrayList;
    import java.util.List;
    
    public class EncodingAdapter 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;
    
        public EncodingAdapter() {
            this(DEFAULT_CHARSET);
        }
    
        public EncodingAdapter(Charset defaultCharset) {
            super(new MediaType[]{
                new MediaType("text", "plain", defaultCharset),
                MediaType.ALL
            });
            this.writeAcceptCharset = true;
            this.defaultCharset = defaultCharset;
            this.availableCharsets = new ArrayList(Charset.availableCharsets().values());
        }
    
        public void setWriteAcceptCharset(boolean writeAcceptCharset) {
            this.writeAcceptCharset = writeAcceptCharset;
        }
    
        public boolean supports(Class <  ?  > clazz) {
            return String.class == clazz;
        }
    
        protected String readInternal(Class <  ? extends String > clazz, HttpInputMessage inputMessage)throws IOException {
            Charset charset = this.getContentTypeCharset(inputMessage.getHeaders().getContentType());
            return StreamUtils.copyToString(inputMessage.getBody(), charset);
        }
    
        protected Long getContentLength(String str, MediaType contentType) {
            Charset charset = this.getContentTypeCharset(contentType);
    
            try {
                return Long.valueOf((long)str.getBytes(charset.name()).length);
            } catch (UnsupportedEncodingException var5) {
                throw new IllegalStateException(var5);
            }
        }
    
        protected void writeInternal(String str, HttpOutputMessage outputMessage)throws IOException {
            if (this.writeAcceptCharset) {
                outputMessage.getHeaders().setAcceptCharset(this.getAcceptedCharsets());
            }
    
            Charset charset = this.getContentTypeCharset(outputMessage.getHeaders().getContentType());
            StreamUtils.copy(str, charset, outputMessage.getBody());
        }
    
        protected List < Charset > getAcceptedCharsets() {
            return this.availableCharsets;
        }
    
        private Charset getContentTypeCharset(MediaType contentType) {
            return contentType != null && contentType.getCharSet() != null ? contentType.getCharSet() : this.defaultCharset;
        }
    }

    //注册方法一:

    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="messageConverters">
    <util:list>
    <bean class="com.ctrip.hotel.h5.common.converter.EncodingAdapter ">
    <constructor-arg index="0" value="UTF-8"/>
    </bean>
    </util:list>
    </property>
    </bean>

    //注册方法二:
    在webconfig.java中:

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
      EncodingAdapter stringConverter = new EncodingAdapter();
      converters.add(0, stringConverter);
    }

    方案四:

    直接新建一个如下的类,放入代码即可。

    package com.h5.common.encode;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    import org.springframework.http.MediaType;
    import org.springframework.http.converter.HttpMessageConverter;
    import org.springframework.http.converter.StringHttpMessageConverter;
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
    import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
    
    import java.nio.charset.Charset;
    import java.util.Arrays;
    import java.util.List;
    
    /**
     * Created by xingyuzhu on 2017/2/27.
     * 解决@ResponseBody返回的响应中中文乱码问题.
     */
    @Component
    public class EncodingPostProcessor implements BeanPostProcessor {
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName)
        throws BeansException{
            if (bean instanceof RequestMappingHandlerAdapter) {
                List < HttpMessageConverter <  ?  >> convs = ((RequestMappingHandlerAdapter)bean).getMessageConverters();
                for (HttpMessageConverter <  ?  > conv : convs) {
                    if (conv instanceof StringHttpMessageConverter) {
                        ((StringHttpMessageConverter)conv).setSupportedMediaTypes(
                            Arrays.asList(new MediaType("text", "html",
                                    Charset.forName("UTF-8"))));
                    }
                }
            }
            if (bean instanceof RequestResponseBodyMethodProcessor) {
                List < HttpMessageConverter <  ?  >> convs = ((RequestMappingHandlerAdapter)bean).getMessageConverters();
                for (HttpMessageConverter <  ?  > conv : convs) {
                    if (conv instanceof StringHttpMessageConverter) {
                        ((StringHttpMessageConverter)conv).setSupportedMediaTypes(
                            Arrays.asList(new MediaType("text", "html",
                                    Charset.forName("UTF-8"))));
                    }
                }
            }
            return bean;
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName)
        throws BeansException{
            return bean;
        }
    }

    方案五:
    在我们的webconfig.java中,注册一个bean:该方法有缺陷RequestMappingHandlerAdapter中的其他messageconverter丢失,导致其他问题,比如返回的是一个jsp页面,就会挂掉

    @Bean
    public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
        RequestMappingHandlerAdapter reqMapHAdapter = new RequestMappingHandlerAdapter();
        ArrayList < HttpMessageConverter <  ?  >> msgConvs = new ArrayList <  > ();
        StringHttpMessageConverter stringConverter = new
            StringHttpMessageConverter(Charset.forName("UTF-8"));
        stringConverter.setSupportedMediaTypes(Arrays.asList(MediaType.TEXT_PLAIN));
        msgConvs.add(stringConverter);
        reqMapHAdapter.setMessageConverters(msgConvs);
        return reqMapHAdapter;
    }

    方案六:
    在webconfig.java中:

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
      StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(Charset.forName("UTF-8"));
      converters.add(0, stringConverter);
    }

    方案七:(篡改框架的编码,推荐使用)
    在webconfig.java中,篡改一下StringHttpMessageConverter的编码方式

    @Override
    public void extendMessageConverters(List < HttpMessageConverter <  ?  >> converters) {
        HttpMessageConverter converter = Iterables.find(converters, new Predicate < HttpMessageConverter <  ?  >> () {
                 @ Override
                public boolean apply( @ Nullable HttpMessageConverter <  ?  > input) {
                    return input != null && input instanceof StringHttpMessageConverter;
                }
            }, null);
        if (converter == null) {
            StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
            stringConverter.setSupportedMediaTypes(Arrays.asList(new MediaType("text", "html", Charset.forName(UTF8))));
            converters.add(1, stringConverter); //默认的StringHttpMessageConverter在第二个位置
            return;
        }
        StringHttpMessageConverter stringHttpMessageConverter = (StringHttpMessageConverter)converter;
        stringHttpMessageConverter.setSupportedMediaTypes(Arrays.asList(new MediaType("text", "html", Charset.forName(UTF8))));
    }
  • 相关阅读:
    uniapp中pages文件及iconfont引入
    js判断字符串是否为JSON格式
    分布式事务CAP简介
    C#面试题
    关于跨域问题
    JS显示隐藏节点
    JS时间序列化显示
    前端命令
    Java8
    java14环境变量配置超简单
  • 原文地址:https://www.cnblogs.com/striver-zhu/p/7158623.html
Copyright © 2020-2023  润新知