1、MessageConverter规范
HttpMessageConverter: 看是否支持将 此 Class类型的对象,转为MediaType类型的数据。
2、默认的MessageConverter
0 - 只支持Byte类型的
1 - String
2 - String
3 - Resource
4 - ResourceRegion
5 - DOMSource.class \ SAXSource.class) \ StAXSource.class \StreamSource.class \Source.class
6 - MultiValueMap
7 - 所有类型
8 - 所有类型
9 - 支持注解方式xml处理的。
最终 MappingJackson2HttpMessageConverter 把对象转为JSON(利用底层的jackson的objectMapper转换的)
3、内容协商
根据客户端接收能力不同,返回不同媒体类型的数据。
1、引入xml依赖
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
2、postman分别测试返回json和xml
只需要改变请求头中Accept字段。Http协议中规定的,告诉服务器本客户端可以接收的数据类型。
3、开启浏览器参数方式内容协商功能
- 开启基于请求参数的内容协商功能。
spring:
contentnegotiation:
favor-parameter: true #开启请求参数内容协商模式
发请求: http://localhost:8080/user?format=json 、http://localhost:8080/user?format=xml
确定客户端接收什么样的内容类型;
1、Parameter策略优先确定是要返回json数据(获取请求头中的format的值)
2、最终进行内容协商返回给客户端json即可。
4、内容协商原理
1、判断当前响应头中是否已经有确定的媒体类型。MediaType
2、获取客户端(PostMan、浏览器)支持接收的内容类型。(获取客户端Accept请求头字段)【application/xml】
3、contentNegotiationManager 内容协商管理器 (有两种策略)
- HeaderContentNegotiationStrategy(请求头的内容协商策略,默认使用)
HeaderContentNegotiationStrategy 确定客户端可以接收的内容类型,然后根据权重返回相应的类型
- ParameterContentNegotiationStrategy(请求参数的内容协商策略))
基于上述浏览器参数方式内容协商功能,客户端可以根据请求参数指定服务器返回的数据类型,例如:http://localhost:8080/user?format=xml(指定服务器数据以xml形式返回)
3、遍历循环所有当前系统的 MessageConverter,看谁支持操作这个对象(Person)
4、找到支持操作Person的converter,把converter支持的媒体类型统计出来。
5、客户端需要【application/xml】。服务端能力【10种、json、xml】
6、进行内容协商的最佳匹配类型
7、用 支持 将对象转为 最佳匹配类型 的converter。调用它进行转化 。
导入了jackson处理xml的包,xml的converter就会自动进来
WebMvcConfigurationSupport
jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
if (jackson2XmlPresent) {
Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.xml();
if (this.applicationContext != null) {
builder.applicationContext(this.applicationContext);
}
messageConverters.add(new MappingJackson2XmlHttpMessageConverter(builder.build()));
}
5、自定义 MessageConverter
- 实现多协议数据兼容。json、xml、x-wust(自定义的协议)
- @ResponseBody 响应数据出去 调用 RequestResponseBodyMethodProcessor 处理
- Processor 处理方法返回值。通过 MessageConverter 处理
- 所有 MessageConverter 合起来可以支持各种媒体类型数据的操作(读、写)
- 内容协商找到最终的 messageConverter;
有可能我们添加的自定义的功能会覆盖默认很多功能,导致一些默认的功能失效。
- 编写User实体类
@Data
public class User {
private int id;
private String name;
private int age;
}
- 编写Controller
@Controller
public class MyController {
@RequestMapping("/user")
@ResponseBody
public User user(){
User user = new User();
user.setId(1);
user.setName("张三");
user.setAge(22);
return user;
}
}
- 编写MyConfig配置类,增加自定义内容协商策略以及消息转换器
@Configuration
public class MyConfig {
@Bean
public WebMvcConfigurer myConfigurer(){
return new WebMvcConfigurer() {
//增加自定义内容协商策略
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
Map<String,MediaType> map = new HashMap<>();
map.put("wust",MediaType.parseMediaType("application/x-wust"));
//因为这个配置默认会覆盖springboot默认的配置,所以我们需要对参数内容协商策略加上原有的
// springboot默认的对json以及xml的处理策略
map.put("json",MediaType.APPLICATION_JSON);
map.put("xml",MediaType.APPLICATION_ATOM_XML);
ParameterContentNegotiationStrategy strategy = new ParameterContentNegotiationStrategy(map);
strategy.setParameterName("f");//设置format,默认为format
configurer.strategies(Arrays.asList(strategy));
}
//配置这个会默认全面覆盖springboot默认的MessageConverter,只保留当前的
// @Override
// public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
//
// }
//配置这个是在springboot的基础上进行扩展的MessageConverter
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new HttpMessageConverter<User>() {
@Override
public boolean canRead(Class<?> aClass, MediaType mediaType) {
return false;
}
//判断是否可写
@Override
public boolean canWrite(Class<?> aClass, MediaType mediaType) {
return aClass.isAssignableFrom(User.class);
}
//返回消息转换器支持的类型
@Override
public List<MediaType> getSupportedMediaTypes() {
//返回自定义的类型
return MediaType.parseMediaTypes("application/x-wust");
}
@Override
public User read(Class<? extends User> aClass, HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException {
return null;
}
//将数据通过输出流以指定的方式返回给客户端
@Override
public void write(User user, MediaType mediaType, HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException {
OutputStream body = httpOutputMessage.getBody();
//自定义返回的样式
String res = user.getId()+";"+user.getName()+";"+user.getAge();
body.write(res.getBytes());
}
});
}
};
}
}
- 测试
方式1:http://localhost:8080/user?f=json
结果:{"id":1,"name":"张三","age":22}
方式2:http://localhost:8080/user?f=xml
结果:<User><id>1</id><name>张三</name><age>22</age></User>
方式3:http://localhost:8080/user?f=wust
结果:1;张三;22