首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >@ResponseBody 总是乱码?这个问题得好好捋一捋!

@ResponseBody 总是乱码?这个问题得好好捋一捋!

作者头像
江南一点雨
发布2019-07-10 18:36:50
9040
发布2019-07-10 18:36:50
举报
文章被收录于专栏:玩转JavaEE玩转JavaEE

本文作者:忧郁的马赛克

本文地址:http://1t.click/cDY

在这篇文章的最后,松哥提了一句,“还有一些非常偶尔的情况可能会用到 @RequestMapping 注解中的 produces 属性”,但是对于这个问题并没有展开讲,有的小伙伴可能还是会遇到这个问题,因此今天就来说一说这个话题。

问题背景

本文并不是介绍 @ResponseBody 注解,也不是中文乱码问题的大汇总笔记,这些网上都有很多内容了。这边仅对几年前,一个卡壳了挺久时间的问题的解决过程做一个记录,以警惕自己,达到自醒得目的。  

@ResponseBody 注解不用多介绍了,用过 SpringMVC 的同学都很熟了, @ResponseBody 将内容或对象作为 HTTP 响应正文返回,使用 @ResponseBody 将会跳过视图处理部分,而是调用适合的 HttpMessageConverter ,将返回值写入输出流。在日常工作中,通常使用封装好的 ViewModel 进行后台数据的返回,一切正常。但一次在使用 @ResponseBody 进行返回 String 数据的时候,竟会出现中文乱码。

编程的过程免不了遇到各种问题,而遇到问题然后解决问题的这个过程我认为是最让人兴奋的事情。越棘手的问题,解决以后带来的快感也越大(PS:当然解决不了的话,就会越烦躁。。),还是言归正传,谈一下解决错误的过程。

问题分析

最早我一直以为 Spring 配置一下编码过滤器就可以解决任何中文乱码问题,恩,确实一直是这样设置着,然并卵,代码如下

<filter>  <filter-name>characterEncodingFilter</filter-name>  <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>  <init-param>    <param-name>encoding</param-name>    <param-value>UTF-8</param-value>  </init-param>  <init-param>    <param-name>forceRequestEncoding</param-name>    <param-value>true</param-value>  </init-param>  <init-param>    <param-name>forceResponseEncoding</param-name>    <param-value>true</param-value>  </init-param></filter><filter-mapping>  <filter-name>characterEncodingFilter</filter-name>  <url-pattern>/*</url-pattern></filter-mapping>

直接返回 String 会乱码,而返回 ViewAndModel 的那个不会乱码,这是为什么?

其实也可以说是 SpringMVC 的一个 bug ,SpringMVC 有一系列 HttpMessageConverter 去处理用 @ResponseBody 注解的返回值,如返回 VM 则使用 MappingJacksonHttpMessageConverter 或者其他的 HttpMessageConverter ,若返回 String ,则使用 StringHttpMessageConverter ,这个 convert 使用的是字符集是 iso-8859-1 ,而且是 final 的,部分源码如下:

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

既然是 String 有问题,那自然就直接从适配器 AnnotationMethodHandlerAdapter 的字符串解析器 StringHttpMessageConverter 入手,设置编码类型即可?NO!

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">   <property name="messageConverters">    <list>     <bean class="org.springframework.http.converter.StringHttpMessageConverter">      <property name="supportedMediaTypes">       <list>        <value>text/plain;charset=UTF-8</value>       </list>      </property>     </bean>

那万金油 response.setContentType("text/html; charset=UTF-8"); 呢?NO!

那么重写 StringHttpMessageConverter 应该可以了吗?NO!

上面方法都不行,就尝试着各种百度,说法多种多样,但答案还是:NO!

问题解决

看来我们得从源头开始再理一遍,既然问题在解析器,那么可以从配置文件配置解析器入手。mvc-config.xml 文件从上到下:控制层扫描、国际化配置、文件上传表单解析器、自定义拦截器、视图配置。好像都不是,继续往下,一些 HandlerMapping 和 HandlerAdapter ,还有一句 <mvc:annotation-driven/>

网上查阅了一下资料,果然发现问题其实就在这句 <mvc:annotation-driven/>

<mvc:annotation-driven/> 是一种简写模式,它会自动注册 DefaultAnnotationHandlerMappingAnnotationMethodHandlerAdapter 两个 bean ,是 SpringMVC@Controller 分发请求所必须的,并且提供了其他一些支持。

上面使用为 StringHttpMessageConverter 设置编码模式其实正常是有效的,但是在使用了 <mvc:annotation-driven/> 语句后,再次显示声明其他 bean ,可能就无效了。

<mvc:annotation-driven>    <mvc:message-converters>        <bean class="org.springframework.http.converter.StringHttpMessageConverter">            <constructor-arg value="UTF-8" />        </bean>    </mvc:message-converters></mvc:annotation-driven>

扩展:其他解决方案

除了使用上面那种方案之外,还可以使用下面的: 

  1. 完全不使用 String 返回,直接都通过统一的 ViewAndModel 去返回,如 ResultModel 等(正常也是这么干的,总还要错误信息等吧);
  2. 通过 @RequestMapping 的属性处理,该注解的属性 produces 用于指定返回的内容类型,这算肯定可以了,代码如下:
@RequestMapping(value="/test", method=RequestMethod.POST, produces="text/html;charset=UTF-8")

注意:既然使用了配置 <mvc:annotation-driven> ,还是建议在该配置内部进行处理。

好了,大家有没有 Get 到知识点呢?

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 江南一点雨 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问题背景
  • 问题分析
  • 问题解决
  • 扩展:其他解决方案
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档