Spring MVC POST 请求乱码:原因与完整解决方案
POST 请求中文乱码是 Spring MVC 开发中的高频问题。前端提交的中文到了后端变成问号、乱码,或者后端返回给前端的中文显示异常,本质原因通常只有一个:请求体、响应体在编码和解码时使用的字符集不一致。
Tomcat 默认使用 ISO-8859-1 解码请求体,而前端通常使用 UTF-8 编码。两边不统一,中文就会乱码。解决思路是让整条链路统一使用 UTF-8:前端请求、Spring 过滤器、Tomcat 连接器、JVM、响应转换器、数据库连接,每一环都要确认。
下面按项目类型和问题环节分别说明。
一、Spring Boot 项目:优先使用配置
Spring Boot 提供了开箱即用的编码配置,在 application.properties 或 application.yml 中添加:
properties
server.servlet.encoding.charset=UTF-8
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
force=true 是关键,它会强制把请求和响应的编码覆盖为 UTF-8。
注意旧版本中的 spring.http.encoding.* 已经废弃,不要继续使用。
如果使用 YAML:
yaml
server:
servlet:
encoding:
charset: UTF-8
enabled: true
force: true
对于大多数 Spring Boot 项目,这一组配置就能解决 POST 表单和普通请求的乱码问题。
二、传统 Spring MVC 项目:注册 CharacterEncodingFilter
传统 Spring MVC 项目需要手动注册 CharacterEncodingFilter。
方式一:web.xml
xml
<filter>
<filter-name>encodingFilter</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>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
这个过滤器必须配置在其他过滤器之前,否则可能被其他过滤器提前读取请求参数,导致编码设置失效。
方式二:Java Config
java
@Bean
public FilterRegistrationBean<CharacterEncodingFilter> encodingFilter() {
FilterRegistrationBean<CharacterEncodingFilter> bean = new FilterRegistrationBean<>();
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
filter.setForceEncoding(true);
bean.setFilter(filter);
bean.addUrlPatterns("/*");
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
setOrder(Ordered.HIGHEST_PRECEDENCE) 保证它在过滤器链最前面执行。
三、Tomcat 与 JVM 层面
如果过滤器已经配置,仍然有乱码,检查 Tomcat 的 conf/server.xml 中 <Connector> 配置:
xml
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="UTF-8"
useBodyEncodingForURI="true" />
URIEncoding="UTF-8":统一 URI 的解码字符集,影响 GET 参数。useBodyEncodingForURI="true":让请求体使用request.setCharacterEncoding()设置的编码。
同时建议给 JVM 加上默认编码参数。Linux/macOS 在 bin/setenv.sh 中:
bash
export JAVA_OPTS="$JAVA_OPTS -Dfile.encoding=UTF-8"
Windows 在 catalina.bat 中:
bat
set JAVA_OPTS=%JAVA_OPTS% -Dfile.encoding=UTF-8
这可以避免日志、文件路径等出现乱码。
四、JSON 请求体乱码
如果乱码发生在 @RequestBody 接收的 JSON 参数上,先检查客户端请求头:
http
Content-Type: application/json;charset=UTF-8
如果客户端无法修改,可以在服务端强制 Jackson 使用 UTF-8:
java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter jacksonConverter =
(MappingJackson2HttpMessageConverter) converter;
jacksonConverter.setDefaultCharset(StandardCharsets.UTF_8);
}
}
}
}
这样即使客户端没有在 Content-Type 中声明 charset,Jackson 也会按 UTF-8 读取请求体。
五、响应乱码
如果后端返回的 JSON 或字符串乱码,可以从两个方向处理。
1. 指定 produces
java
@RequestMapping(value = "/api", produces = "application/json;charset=UTF-8")
2. 调整 StringHttpMessageConverter
java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(0, new StringHttpMessageConverter(StandardCharsets.UTF_8));
}
}
将 UTF-8 的 StringHttpMessageConverter 放在最前面,避免默认转换器使用其他编码。
六、数据库连接乱码
如果数据到了数据库才乱码,检查 JDBC URL:
jdbc:mysql://localhost:3306/db?useUnicode=true&characterEncoding=UTF-8
同时确认数据库和表的字符集:
sql
SHOW CREATE TABLE your_table;
推荐使用 utf8mb4,兼容中文和 emoji。
七、排查清单
出现 POST 乱码时,按下面顺序排查:
| 检查位置 | 关键点 |
|---|---|
| 前端请求 | Content-Type 是否包含 charset=UTF-8 |
| Spring Boot 配置 | server.servlet.encoding.force=true |
| 传统项目过滤器 | CharacterEncodingFilter 是否生效,是否在最前面 |
| Tomcat 连接器 | URIEncoding="UTF-8"、useBodyEncodingForURI="true" |
| JVM 参数 | -Dfile.encoding=UTF-8 |
| JSON 请求体 | Jackson 是否按 UTF-8 读取 |
| 响应转换器 | StringHttpMessageConverter 或 produces |
| 数据库 | JDBC URL 编码、库表字符集 |
八、总结
POST 请求乱码不是单一配置能覆盖所有场景的问题,它涉及前端、Spring、Tomcat、JVM、数据库多个环节。解决的核心原则是:保证请求、处理、响应、存储每一环都使用 UTF-8。
实际项目中可以按这个优先级处理:
- Spring Boot 项目先加
server.servlet.encoding.*配置。 - 传统项目优先检查
CharacterEncodingFilter。 - 仍有问题时检查 Tomcat 连接器和 JVM 编码。
- JSON 乱码单独处理 Jackson 的默认字符集。
- 响应乱码检查
StringHttpMessageConverter和produces。 - 数据库乱码检查 JDBC URL 和库表字符集。
按这个顺序排查,绝大多数 POST 中文乱码都能定位并解决。