本文是我的专栏《学透Spring Boot》的第17篇文章,了解更多请移步我的专栏:
学透 Spring Boot_postnull咖啡的博客-CSDN博客
目录
HTTP请求和响应
很多接口,我们发起HTTP请求,请求参数是json。得到的响应也是json。
但是我们的控制器中,是使用Java对象来接收请求,出参也是Java对象。
而不是JSONObject。
这样的好处是更好操作,不用再次把Json对象转换成Java对象。
这是怎么做到的呢?
是不是和上一篇的Spring MVC Conversion Service 类型转换 一样的原理呢?
是,但不完全是。
需求—新的Media Type
先来实现一个需求,看看能不能实现。
我们希望我们的请求是这样
和普通的请求不太一样,主要有亮点:
- 我们的MediaType是自己定义“hehe/nba”
- 我们的数据体是自己构造的文本,用###分割字段
一般情况,没人会自定定义媒体类型,用得最多的是xml和json。
我们这里这么做,是为了理解json消息体是怎么解析的。
实现—新的Media Type
定义转换器
首先我们先定义一个新Http消息转换器。
它继承的是HttpMessageConverter接口。
注意这里实现的是HttpMessageConverter接口,和我们上一篇文章的类型转换器不一样,它实现的是Converter接口。
public class CarHttpConverter implements HttpMessageConverter<Car> {
private static final String SPLITCHAR = "###";
private static final String MY_MEDIA_TYPE1 = "hehe/nba;charset=UTF-8";
private static final String MY_MEDIA_TYPE2 = "hehe/nba";
@Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
if(mediaType == null){
return true;
}
return clazz == Car.class
&& (mediaType.equals(MediaType.valueOf(MY_MEDIA_TYPE1)) || mediaType.equals(MediaType.valueOf(MY_MEDIA_TYPE2)));
}
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
if(mediaType == null){
return true;
}
return clazz == Car.class
&& (mediaType.equals(MediaType.valueOf(MY_MEDIA_TYPE1)) || mediaType.equals(MediaType.valueOf(MY_MEDIA_TYPE2)));
}
@Override
public List<MediaType> getSupportedMediaTypes() {
return List.of(MediaType.valueOf(MY_MEDIA_TYPE1), MediaType.valueOf(MY_MEDIA_TYPE2));
}
@Override
public Car read(Class<? extends Car> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
// 从输入流中读取 CSV 数据并将其转换为 Book 对象
InputStreamReader reader = new InputStreamReader(inputMessage.getBody());
StringBuilder csvData = new StringBuilder();
int character;
while ((character = reader.read()) != -1) {
csvData.append((char) character);
}
String[] fields = csvData.toString().split(SPLITCHAR);
String type = fields[0];
double price = Double.parseDouble(fields[1]);
String year = fields[2];
return Car.builder().type(type).price(price).year(year).build();
}
@Override
public void write(Car car, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
OutputStreamWriter writer = new OutputStreamWriter(outputMessage.getBody());
String csvData = car.getType() + SPLITCHAR + car.getPrice() + SPLITCHAR + car.getYear();
writer.write(csvData);
writer.flush();
}
}
注册转换器
然后,注册这个Http消息转换器
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new CarHttpConverter());
}
}
编写Controller
特别注意,我们这里的consum和produce用的media type是我们自定义的type。
consumes 指定接收HTTP请求的mediaytpe
produces 指定发送HTTP响应的mediatype
@RestController
@Log
public class HttpMsgController {
@PostMapping(path = "/buyCar", consumes = "hehe/nba", produces = "hehe/nba")
public Car buyCar(@RequestBody Car car){
car.setYear("2025");
return car;
}
}
测试新的mediatype
设置content type。hehe/nba是我们自定义的type。
设置request body,我们的内容是用###做分隔符的。
这是我们约定的格式。
大功告成!
我们没有使用json,但是定了一个一种新的序列化格式!!!hehe/nba!
Http消息转换器实现原理
我们可以debug看看消息转换器注册的代码。
converters列表中包含了我们新定义的转换器,还包括了Jackson的消息转换器。
我们再看看Spring Boot是如何自动配置Jackson的。
找到Spring Boot的配置类列表
怎么定位这个文件,请参考我之前的文章。
列表中包含了Spring MVC的自动配置WebMvcAutoConfiguration
大部分的默认配置在WebMvcConfigurationSupport。
可以看到,当我们的classpath下,有jackson的包,就会自动使用Jackson处理requestbody和response body.
总结
本文我们定义了一个新的content type, 构造新的请求体和响应体。希望通过本文,你对Http 消息转换器有更多的了解。