《经久不衰的Spring框架:@ResponseBody 中文乱码》
问题背景
本文并不是介绍@ResponseBody注解,也不是中文乱码问题的大汇总笔记,这些网上都有很多内容了。这边仅对几年前,一个卡壳了挺久时间的问题的解决过程做一个记录,以警惕自己,达到自醒得目的。
@ReponseBody 注解不用多介绍了,用过SpringMVC的同学都很熟了,@ResponseBody 将内容或对象作为 HTTP 响应正文返回,使用@ResponseBody将会跳过视图处理部分,而是调用适合的HttpMessageConverter,将返回值写入输出流。在日常工作中,通常使用封装好的ViewModel进行后台数据的返回,一切正常。但一次在使用@ReponseBody进行返回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>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
直接返回String会乱码,而返回ViewModel的那个不会乱码,这是为什么?
其实也可以说是SpringMVC的一个bug,SpringMVC有一系列HttpMessageConverter去处理用@ResponseBody注解的返回值,如返回VM则使用MappingJacksonHttpMessageConverter,若返回String,则使用StringHttpMessageConverter,这个convert使用的是字符集是iso-8859-1,而且是final的,如下:
public static final Charset DEFAULT_CHARSET = Charset.forName("ISO-8859-1");
既然是String有问题,那自然就直接从适配器AnnotationMethodHandlerAdapter 的字符串解析器 StringHttpMessageConverter 入手,设置编码类型即可?
<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>
...
NO!
那万金油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 /> 是一种简写模式,它会自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter 两个bean,是spring MVC为@Controllers分发请求所必须的,并且提供了其他一些支持。。。略
上面使用为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返回,直接都通过统一的ViewModel去返回,如ResultModel等(正常也是这么干的,总还要错误信息等吧);
2、通过@RequestMapping的属性处理,该注解的属性produces用于指定返回的内容类型,这算肯定可以了,代码如下:
@RequestMapping(value="/test", method=RequestMethod.POST, produces="text/html;charset=UTF-8")
注意:既然使用了配置<mvc:annotation-driven>,还是建议在该配置内部进行处理。
编后语
上面的问题记录是很多多年前的了,现在翻到博客上,只是为了告诫自己:
天下文章一大抄,但是抄来抄去,不管是写的人,还是看的人,自己最好都能理解或者亲自去尝试一下;这个乱码问题网上总结太多太多了,不能说是错误的,但是又有几个正确得说清楚呢?
如果放在现在来解决这个问题,最好的方案还是直接跟一下SpringMVC的源码,简单明了。String类型的返回值处理会进入StringHttpMessageConverter解析器,观察getContentTypeCharset方法的参数,可以看到当前解析编码是text/plain;charset=ISO-8859-1,即设置不成功,设置不成功可以继续看看适配器AnnotationMethodHandlerAdapter的属性设置,一层层反推,总会发现问题的。
这里奉劝各位兄弟姐妹,网上的东西还是要仔细研究的,不要转来转去的。
《经久不衰的Spring框架:@ResponseBody 中文乱码》的更多相关文章
- 简单物联网:外网访问内网路由器下树莓派Flask服务器
最近做一个小东西,大概过程就是想在教室,宿舍控制实验室的一些设备. 已经在树莓上搭了一个轻量的flask服务器,在实验室的路由器下,任何设备都是可以访问的:但是有一些限制条件,比如我想在宿舍控制我种花 ...
- 利用ssh反向代理以及autossh实现从外网连接内网服务器
前言 最近遇到这样一个问题,我在实验室架设了一台服务器,给师弟或者小伙伴练习Linux用,然后平时在实验室这边直接连接是没有问题的,都是内网嘛.但是回到宿舍问题出来了,使用校园网的童鞋还是能连接上,使 ...
- 外网访问内网Docker容器
外网访问内网Docker容器 本地安装了Docker容器,只能在局域网内访问,怎样从外网也能访问本地Docker容器? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Docker容器 ...
- 外网访问内网SpringBoot
外网访问内网SpringBoot 本地安装了SpringBoot,只能在局域网内访问,怎样从外网也能访问本地SpringBoot? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装Java 1 ...
- 外网访问内网Elasticsearch WEB
外网访问内网Elasticsearch WEB 本地安装了Elasticsearch,只能在局域网内访问其WEB,怎样从外网也能访问本地Elasticsearch? 本文将介绍具体的实现步骤. 1. ...
- 怎样从外网访问内网Rails
外网访问内网Rails 本地安装了Rails,只能在局域网内访问,怎样从外网也能访问本地Rails? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Rails 默认安装的Rails端口 ...
- 怎样从外网访问内网Memcached数据库
外网访问内网Memcached数据库 本地安装了Memcached数据库,只能在局域网内访问,怎样从外网也能访问本地Memcached数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装 ...
- 怎样从外网访问内网CouchDB数据库
外网访问内网CouchDB数据库 本地安装了CouchDB数据库,只能在局域网内访问,怎样从外网也能访问本地CouchDB数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Cou ...
- 怎样从外网访问内网DB2数据库
外网访问内网DB2数据库 本地安装了DB2数据库,只能在局域网内访问,怎样从外网也能访问本地DB2数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动DB2数据库 默认安装的DB2 ...
- 怎样从外网访问内网OpenLDAP数据库
外网访问内网OpenLDAP数据库 本地安装了OpenLDAP数据库,只能在局域网内访问,怎样从外网也能访问本地OpenLDAP数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动 ...
随机推荐
- X-007 FriendlyARM tiny4412 u-boot移植之内存初始化
<<<<<<<<<<<<<<<<<<<<<<<<< ...
- Java Dwr3实现消息推送步骤详解
DWR包含两个主要的部分:允许JavaScript从WEB服务器上一个遵循了AJAX原则的Servlet中获取数据.另外一方面一个JavaScript库可以帮助网站开发人员轻松地利用获取的数据来动态改 ...
- MVC笔记1
测试实体类 public class Test { public int id{get;set;} [Required(ErrorMessage="不能为空喔")] [String ...
- 在ubuntu中为navicat创建快捷方式
在ubuntu中,解压navicat并不会生成快捷方式,每次运行都需要进入软件解压的目录,然后运行命令开启navicat,十分不便.今天尝试引入快捷方式,直接双击运行,感觉挺不错. 首先下载一个合适的 ...
- 高尔夫 之 TEE
黑色:男子职业选手蓝色:男子业余高手.女子职业选手白色:男子初学球手.老年球手.女子业余高手红色:女子选手金色:有的球场做为介于 黑Tee (职业球员)与 蓝Tee (业余高手)之间的Tee 台:有的 ...
- OC-Objection 学习笔记之一:简单的开始
Objection 统一管理对象的引用问题,我想这就是这种技术的意义吧. 废话不说,咱们直接上步骤吧: 1:协议 我们的意识里要知道,一切围绕协议来进行. 下面的协议是一个视图的协议,该协议简单到不能 ...
- 创建TabBar
-(void)creatTabBarView { NSArray *imgArray=@[]; NSArray *selectImage=@[]; NSArray *names=@[]; for (i ...
- iPhone的刷机 iPhone进UDF
首先是进入udf出现问题.. 要点是 黑屏后按电源键到屏幕亮了+HOME键再一直按 按到黑屏 4秒左右后 放开HOME键 再按大概7秒就是udf界面了 不是界面~全黑的.你可以电源键是开不了机的. 然 ...
- WinAPI: GetClassName - 获取指定窗口的类名
WinAPI: GetClassName - 获取指定窗口的类名 //声明: GetClassName( hWnd: HWND; {指定窗口句柄} lpClassName: PChar; {缓冲区} ...
- loading.io一个loading图标网站,跟大家分享
loading.io是官方网址在首页选一款loading图标,看到左上角的 Try it now中有选中的图标后,可通过光标滑动选择图标大小,然后再点右边的get svg或get css等下载即可