Spring 中文响应处理
Contents
为什么 CharacterEncodingFilter
没有生效
<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>
这个配置, 只有在 Controller 里,调用 HttpServletResponse 直接写数据时才会生效的 .如果是直接利用HttpServletResponse
,就不能再用@ResponseBody
了,例如下面这样子写就会报错:
@RequestMapping("/index")
@ResponseBody
public String index(HttpServletResponse response) throws IOException {
response.getWriter().write("你好,中文");
return "中文";
}
错误为:
Caused by:
java.lang.IllegalStateException: WRITER
at org.eclipse.jetty.server.Response.getOutputStream(Response.java:706)
at javax.servlet.ServletResponseWrapper.getOutputStream(ServletResponseWrapper.java:142)
at org.springframework.session.web.http.OnCommittedResponseWrapper.getOutputStream(OnCommittedResponseWrapper.java:124)
直接用 HttpServletResponse
写数据
@RequestMapping("/index")
public void index(HttpServletResponse response) throws IOException {
response.getWriter().write("你好,中文");
}
➜ ~ curl http://localhost:8080/index
你好,中文
➜ ~
使用@ResponseBody
@RequestMapping("/index")
@ResponseBody
public String index(HttpServletResponse response) throws IOException {
return "你好,中文";
}
➜ ~ curl http://localhost:8080/index
?????% ➜ ~
可以看到,返回的是乱码了.
原因
因为默认情况下,如果Controller返回的是String的@ResponseBody
话,Spring会调用默认的StringHttpMessageConverter
来返回String,而这个StringHttpMessageConverter
默认情况下,只是使用public static final Charset DEFAULT_CHARSET = Charset.forName("ISO-8859-1");
这个字符集,所以就会报错了.
解决
方法一:添加指定的MessageConverter
<mvc:annotation-driven>
<!-- register custom converter that returns UTF-8 encoded response-body by defualt -->
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg index="0" name="defaultCharset" value="UTF-8"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
注意register-defaults
这表示是否也注册默认的HttpMessageConverter
(默认也是true
)
这样子,就注册多了一个StringHttpMessageConverter
了,默认情况下的HttpMessageConverter
有以下9个:
默认注册的 HttpMessageConverter
0 = {ByteArrayHttpMessageConverter@6398}
1 = {StringHttpMessageConverter@6399}
2 = {ResourceHttpMessageConverter@6400}
3 = {SourceHttpMessageConverter@6401}
4 = {AllEncompassingFormHttpMessageConverter@6402}
5 = {AtomFeedHttpMessageConverter@6403}
6 = {RssChannelHttpMessageConverter@6404}
7 = {Jaxb2RootElementHttpMessageConverter@6405}
8 = {MappingJacksonHttpMessageConverter@6406}
注意
Jackson
的,要添加相关的Jackson
的依赖包.
现在添加多了一个的话,就有10个了:
0 = {StringHttpMessageConverter@6398}
defaultCharset = {UTF_8@6408} "UTF-8"
availableCharsets = {ArrayList@6409} size = 170
writeAcceptCharset = true
logger = {SLF4JLocationAwareLog@6410}
supportedMediaTypes = {ArrayList@6411} size = 2
1 = {ByteArrayHttpMessageConverter@6399}
2 = {StringHttpMessageConverter@6400}
defaultCharset = {ISO_8859_1@6413} "ISO-8859-1"
availableCharsets = {ArrayList@6414} size = 170
writeAcceptCharset = false
logger = {SLF4JLocationAwareLog@6410}
supportedMediaTypes = {ArrayList@6415} size = 2
3 = {ResourceHttpMessageConverter@6401}
4 = {SourceHttpMessageConverter@6402}
5 = {AllEncompassingFormHttpMessageConverter@6403}
6 = {AtomFeedHttpMessageConverter@6404}
7 = {RssChannelHttpMessageConverter@6405}
8 = {Jaxb2RootElementHttpMessageConverter@6406}
9 = {MappingJacksonHttpMessageConverter@6407}
可以看到,我们自己定义的StringHttpMessageConverter
放在第一位了,字符集为UTF-8
,而Spring默认的SpringHttpMessageConverter
为ISO-8859
。
这时可以看到可以返回正常的中文了:
@RequestMapping("/index")
@ResponseBody
public String index(HttpServletResponse response) throws IOException {
return "你好,中文";
}
➜ ~ curl http://localhost:8080/index
你好,中文% ➜ ~
方法二:指定 produces
@RequestMapping(value = "/index", produces = "text/plain; charset=utf-8")
@ResponseBody
public String index(HttpServletResponse response) throws IOException {
return "你好,中文";
}
➜ ~ curl http://localhost:8080/index
你好,中文% ➜ ~
这样子,就算不配置指定UTF-8
字符集的StringHttpMessageConverter
,也可以正常返回中文了。因为源码里有处理,如果有指定charset
的话,就会使用指定的字符集来写数据.这里以StringHttpMessageConverter
为例:
@Override
protected void writeInternal(String s, HttpOutputMessage outputMessage) throws IOException {
if (this.writeAcceptCharset) {
outputMessage.getHeaders().setAcceptCharset(getAcceptedCharsets());
}
Charset charset = getContentTypeCharset(outputMessage.getHeaders().getContentType());
StreamUtils.copy(s, charset, outputMessage.getBody());
}
其他对象的转换器同理.