@RequestBody 与 HttpMessageConverter、JSON 序列化

HTTP 请求体和响应体本质都是字节流。@RequestBody 负责把请求体反序列化成 Java 对象,@ResponseBody 负责把对象序列化成响应体,背后的执行者是 HttpMessageConverter。

一个注解看懂读写

@RestController
public class UserApi {
    @PostMapping("/api/users")
    public User create(@RequestBody User user) {   // 读:JSON → User
        user.setId(1L);
        return user;                                // 写:User → JSON
    }
}
// 请求体:{"name":"张三","age":20}
// 响应体:{"id":1,"name":"张三","age":20}

User 类只要提供无参构造、字段和 getter/setter 即可,字段名与 JSON key 对应。

HttpMessageConverter 是幕后的搬运工

MessageConverter 负责两种方向的转换:

public interface HttpMessageConverter<T> {
    boolean canRead(Class<?> clazz, MediaType mediaType);   // 能不能读这种类型/媒体类型
    boolean canWrite(Class<?> clazz, MediaType mediaType);   // 能不能写
    T read(Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException;
    void write(T t, MediaType contentType, HttpOutputMessage outputMessage) throws IOException;
}
  • 读请求体:按请求的 Content-Type 挑转换器。application/json → Jackson(MappingJackson2HttpMessageConverter,类路径有 Jackson 且开了 @EnableWebMvc 时默认注册)。
  • 写响应体:按客户端 Accept(经内容协商)挑转换器。
  • 常见的还有 StringHttpMessageConverter(text/plain)、ByteArrayHttpMessageConverter、FormHttpMessageConverter(表单),默认注册清单以官方文档为准。

一个请求体对应一个 @RequestBody

一个方法只能有一个 @RequestBody 参数。整个请求体被反序列化成一个对象;要同时收 JSON 和文件请用 @RequestPart(第 31 章)。

自定义 JSON 行为:ObjectMapper

序列化规则由 Jackson 的 ObjectMapper 决定,例如日期格式、忽略未知字段:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
                .serializers(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
                .featuresToDisable(DeserializationFeature.FAIL_ON_UNKNOWN_FIELDS);
        converters.add(0, new MappingJackson2HttpMessageConverter(builder.build()));
    }
}
// 说明:Spring Boot 对 Jackson 有独立自动配置,本文按原生 Spring MVC 处理

常见配套依赖:Jackson 本体是 jackson-databind;序列化 java.time 类型需 jackson-datatype-jsr310(JavaTimeModule,默认处理 LocalDate/LocalDateTime 等)。

写一个自定义 Converter

特殊格式(如 XML 之外的自定义协议)可以实现 HttpMessageConverter 并注册到 converters 列表首位。日常开发里更常见的是用 Gson:

// 依赖 Gson 时,Spring 提供了现成的 GsonHttpMessageConverter(gson 在类路径即可)
converters.add(0, new GsonHttpMessageConverter());

典型错误码

  • 请求体不是合法 JSON / 类型对不上:HttpMessageNotReadableException,默认映射 400。
  • 请求 Content-Type 没有可用转换器:HttpMediaTypeNotSupportedException(415)。
  • 写响应时 Accept 无匹配媒体类型:HttpMediaTypeNotAcceptableException(406)。

@RequestBody/@ResponseBody 只是入口,真正的活是 HttpMessageConverter 干的。写接口时记住三点:对象要能被反序列化(无参构造 + setter)、日期格式先定好、Content-Type 与 Accept 要和转换器对得上。

笔记加载中…