StringHttpMessageConverter 详解:Spring MVC 中字符串读写的默认转换器
一、它是什么
StringHttpMessageConverter 是 Spring MVC 内置的 HttpMessageConverter 实现,专门负责 String 类型与 HTTP 请求/响应体之间的转换。
它的职责非常单一:
- 读:把请求体按字符集解码成
String。 - 写:把
String按字符集编码写入响应体。
它不处理 JSON,不处理对象,只处理字符串。但因为 String 在 Web 开发中极其常见,这个转换器的行为直接影响到很多接口的返回结果。
二、接口实现
java
public class StringHttpMessageConverter extends AbstractHttpMessageConverter<String> {
public static final Charset DEFAULT_CHARSET = StandardCharsets.ISO_8859_1;
private volatile List<Charset> availableCharsets;
public StringHttpMessageConverter() {
this(DEFAULT_CHARSET);
}
public StringHttpMessageConverter(Charset defaultCharset) {
super(defaultCharset, MediaType.TEXT_PLAIN, MediaType.ALL);
}
@Override
protected boolean supports(Class<?> clazz) {
return String.class == clazz;
}
@Override
protected String readInternal(Class<? extends String> clazz,
HttpInputMessage inputMessage) throws IOException {
Charset charset = getContentTypeCharset(inputMessage.getHeaders().getContentType());
return StreamUtils.copyToString(inputMessage.getBody(), charset);
}
@Override
protected void writeInternal(String str, HttpOutputMessage outputMessage)
throws IOException {
Charset charset = getContentTypeCharset(outputMessage.getHeaders().getContentType());
StreamUtils.copy(str, charset, outputMessage.getBody());
}
}
关键点:
| 成员 | 含义 |
|---|---|
DEFAULT_CHARSET |
默认字符集是 ISO_8859_1,不是 UTF-8 |
supports |
只支持 String.class |
MediaType.TEXT_PLAIN |
默认媒体类型是 text/plain |
MediaType.ALL |
同时接受所有媒体类型作为通配 |
readInternal |
按字符集把请求体解码成字符串 |
writeInternal |
按字符集把字符串编码写入响应体 |
三、它在转换器列表中的位置
Spring MVC 默认注册的转换器顺序大致是:
ByteArrayHttpMessageConverter
StringHttpMessageConverter
ResourceHttpMessageConverter
...
MappingJackson2HttpMessageConverter
...
StringHttpMessageConverter 排在 Jackson 之前。
这意味着:当返回值是 String 时,StringHttpMessageConverter 会先被匹配,Jackson 没有机会处理。
这就是为什么:
java
@RestController
public class TestController {
@GetMapping("/text")
public String text() {
return "hello";
}
}
响应体是:
hello
而不是:
json
"hello"
如果希望返回带双引号的 JSON 字符串,必须显式声明 produces = "application/json",或者把返回值包装成对象。
四、什么时候会被选中
1. 写响应时
触发条件:
- 方法返回值类型是
String。 - 客户端
Accept头与text/plain或*/*匹配。 - 没有其他更高优先级的转换器处理
String。
典型场景:
java
@GetMapping("/hello")
public String hello() {
return "hello world";
}
2. 读请求时
触发条件:
- 方法参数是
String,并且使用@RequestBody。 - 请求头
Content-Type是text/plain或兼容类型。
java
@PostMapping("/echo")
public String echo(@RequestBody String body) {
return body;
}
如果客户端发送 Content-Type: application/json,StringHttpMessageConverter 也能匹配,因为构造方法里注册了 MediaType.ALL。但实际项目里不推荐用 String 接 JSON,容易绕过 JSON 校验。
五、字符集问题
这是 StringHttpMessageConverter 最容易踩坑的地方。
1. 默认字符集是 ISO_8859_1
java
public static final Charset DEFAULT_CHARSET = StandardCharsets.ISO_8859_1;
如果响应头没有明确指定 charset,转换器会用 ISO_8859_1 编码字符串。中文字符在这个字符集下无法表示,会被替换成 ?。
2. 什么时候会乱码
java
@GetMapping("/chinese")
public String chinese() {
return "你好";
}
如果响应头是:
Content-Type: text/plain
没有 charset,浏览器按默认编码解析,可能显示乱码。
3. 解决方式
方式一:在请求映射上声明 produces
java
@GetMapping(value = "/chinese", produces = "text/plain;charset=UTF-8")
public String chinese() {
return "你好";
}
方式二:配置 StringHttpMessageConverter 的默认字符集
java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof StringHttpMessageConverter stringConverter) {
stringConverter.setDefaultCharset(StandardCharsets.UTF_8);
}
}
}
}
方式三:Spring Boot 中配置编码
yaml
server:
servlet:
encoding:
charset: UTF-8
force: true
force: true 会强制请求和响应使用 UTF-8,但要注意它作用于整个请求链路,不只影响 StringHttpMessageConverter。
方式四:替换默认转换器
java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(0, new StringHttpMessageConverter(StandardCharsets.UTF_8));
}
}
add(0, ...) 把 UTF-8 版本的转换器放在最前面,覆盖默认的 ISO_8859_1 版本。
六、和 Jackson 转换器的对比
| 对比 | StringHttpMessageConverter | MappingJackson2HttpMessageConverter |
|---|---|---|
| 处理类型 | String |
任意对象 |
| 媒体类型 | text/plain、*/* |
application/json |
| 优先级 | 高 | 低 |
返回 String 时 |
纯文本输出 | 不会轮到它 |
| 字符集默认 | ISO_8859_1 | UTF-8 |
| 典型用途 | 纯文本接口、简单返回值 | JSON 接口 |
一句话记忆:String 先被字符串转换器截胡,Jackson 只处理非 String 对象。
七、完整示例
1. 纯文本返回
java
@RestController
public class TextController {
@GetMapping(value = "/text", produces = "text/plain;charset=UTF-8")
public String text() {
return "这是一段纯文本";
}
}
响应:
Content-Type: text/plain;charset=UTF-8
这是一段纯文本
2. 返回 JSON 字符串
如果确实希望返回带引号的 JSON 字符串:
java
@GetMapping(value = "/json-string", produces = "application/json")
public String jsonString() {
return "hello";
}
因为显式声明 produces = "application/json",StringHttpMessageConverter 的 canWrite 不匹配,Jackson 接手,输出:
json
"hello"
3. 读取纯文本请求体
java
@PostMapping(value = "/echo", consumes = "text/plain")
public String echo(@RequestBody String body) {
return "收到:" + body;
}
客户端:
POST /echo
Content-Type: text/plain;charset=UTF-8
你好
响应:
收到:你好
八、常见问题
1. 返回 String 没有双引号
这是正常行为,不是 bug。StringHttpMessageConverter 把字符串当纯文本输出。需要 JSON 字符串时显式声明 produces = "application/json"。
2. 中文乱码
默认 ISO_8859_1 导致。解决方式是设置 produces 带 charset=UTF-8,或替换转换器的默认字符集。
3. 415 Unsupported Media Type
@RequestBody String 时,请求的 Content-Type 与转换器不匹配。检查客户端是否发送了 text/plain 或兼容类型。
4. 406 Not Acceptable
客户端 Accept 头不接受 text/plain。检查 Accept 头,或后端声明合适的 produces。
5. 自定义转换器被覆盖
如果自定义了 StringHttpMessageConverter 但没有放在列表前面,默认的 ISO_8859_1 版本可能仍然生效。使用 add(0, ...) 或 extendMessageConverters 修改现有实例。
九、记忆方法
1. 一句话定位
String 走字符串转换器,
纯文本输出不加引号,
默认 ISO_8859_1,
中文必须显式 UTF-8。
2. 三个关键数字
1 个类型:String
1 个默认媒体类型:text/plain
1 个默认字符集:ISO_8859_1
3. 优先级口诀
String 在前,Jackson 在后,
返回字符串,纯文本先走。
4. 乱码解决口诀
produces 加 charset,
或者替换默认编码,
Spring Boot 配 force,
三选一就能解决。
5. 和 Jackson 的分界
String → StringHttpMessageConverter
对象 → MappingJackson2HttpMessageConverter
String + produces=json → Jackson 接手
十、总结
| 维度 | 说明 |
|---|---|
| 作用 | String 与 HTTP 请求/响应体之间的转换 |
| 支持类型 | 仅 String |
| 默认媒体类型 | text/plain,同时接受 */* |
| 默认字符集 | ISO_8859_1 |
| 优先级 | 高于 Jackson |
| 返回 String 行为 | 纯文本,不加双引号 |
| 中文乱码原因 | 默认字符集不是 UTF-8 |
| 解决方式 | produces 声明 charset、替换默认字符集、Spring Boot 强制编码 |
| 典型场景 | 纯文本接口、简单字符串返回值 |
StringHttpMessageConverter 是 Spring MVC 中最容易被忽视但影响很直接的转换器。它排在 Jackson 前面,所以返回 String 时永远是纯文本;它默认用 ISO_8859_1,所以中文容易乱码。记住"String 优先、纯文本、ISO_8859_1"这三个关键词,再记住"要 JSON 加 produces,要中文加 charset",这类问题就能快速定位和解决。