上两篇笔记中已经学习到了请求参数相关的处理,文章地址,接下来学习数据响应的代码流程
1、ReturnValueHandler
1)复习一下响应JSON树
(1)过程
(2)响应JSON原理
- 断点打在这里
- 从这里始终会来到这里
- 这里可以看到返回值参数解析器
- 一直点这个会经过这一步
- 继续点刚才标记的那个可以来到这一步
来到了这里【利用返回值处理器进行后续的处理】
这里返回了一个false,然后返回处理器判断是否支持这种数据类型
如果它支持的话就会再调用返回值处理器
在这里会有多次的循环遍历查找【直到找到能处理返回值的那个处理器为止】
最后匹配到了这个处理器
(3)到底是如何写成JSON数据的呢?
上面的过程中我们找到了处理数据的处理器,接下里详细了解一下如何处理的:
-
最后的返回值是由这个来处理的
-
进入这方法看看【其实是利用MessageConverters这个进行处理,然后将数据写成JSON】
来到了这里
-
这里的MediaType媒体类型,和浏览器进行类型协商和浏览器会定义好【就是内容协商】
浏览器会与服务器请求的方式告诉能接受什么的类。 -
内容协商
这里进入浏览器和我能发的进行
这里就会找到到底能写和读
既然往下面走会走到这里来
这里的for循环会找到我们需要写操作的一个类型
0 - 只支持Byte类型的
1 - String
2 - String
3 - Resource
4 - ResourceRegion
5 - DOMSource.class \ SAXSource.class) \ StAXSource.class \StreamSource.class \Source.class
6 - MultiValueMap
7 - true 【不管是什么我都支持】
8 - true【不管是什么我都支持】
9 - 支持注解方式xml处理的。
它会添加点东西,然后再后续的以json方式写出去。
最后的最终 MappingJackson2HttpMessageConverter 把对象转为JSON(利用底层的jackson的objectMapper转换的)
2)内容协商原理
内容协商就是客户端和服务器之间进行协商,到底返回什么样子的数据类型,已达到方便的显示。
(1)看一下使用XML来处理
- 导入依赖
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
- 看响应状态
这样响应的原因是为什么呢【它把xml排在了第二位】
当使用postman测试的时候,就会发现响应的还是JSON,而修改Accept才可以响应XML。
(2)打断点来看
- 打断点发现它根JSON也是一样的都由这个处理
- 同样的走到这一步
-
首先判断当前响应头中是否已经有确定的媒体类型,MediaType
-
没有的话进行一个全新的媒体类型【这里就是通过前端的Accept字段支持的类型进行处理,找到全部的类型】
包装对象,获取Accept字段
-
获取能产生的类型
这里获取到了比如我们刚刚导入的xml的,JSON的 -
在这一步会找到支持xml,JSON的数据converter
converter把支持的类型统计出来。
-
以我们刚刚的xml的请求为例:最后统计出来,客户端能处理xml,而服务端可以处理上面的9种吧。
-
这里做一个最佳匹配【嵌套for循环,应该都知道吧】
-
这里就能相互匹配到
-
这里又要进行进一步的判断到底哪个能将Car对象转换为xml对象
上面是匹配到哪个能处理xml,下面一步又判断到哪个能转换。 -
最后从这里来进行写【这里先不再详细说写的过程】
-
最后返回到了这里
-
具体匹配的要补充一点它是有优先级的
-
(3)小总结
3)基于请求参数的内容协商原理
(1)开启浏览器参数方法内容协商功能
上面介绍的内容协商原理呢是默认采用的请求头进行对比响应的。
(2)在浏览器通过formant设置
(3)那上面面能开启的原理是什么呢
同样的这里你带上了那个参数后,会在后台动态的给你确定了你指定的那个参数类型了
自然而然它内容协商的也会多一个
3)自定义MessageConverter
(1)先看点概念
实现多协议数据兼容。json、xml、自定义数据类型、@ResponseBody 响应数据出去 调用 RequestResponseBodyMethodProcessor 处理
- Processor 处理方法返回值。通过 MessageConverter 处理。
- 所有 MessageConverter 合起来可以支持各种媒体类型数据的操作(读、写)。
- 内容协商找到最终的 messageConverter;
(2)响应数据
- 浏览器请求的话响应我们的JSON
- 如果是AJAX的话,我们响应xml
- 如果是自定义数据类型的话,我们响应自定义数据类型。
(3)为什么能有那么多的Converter呢
- 先去找找为什么能启动就有那么多默认的Converter
经过层层找下去,原来是在这里
只要导入了xml,JSON依赖就能自动启用的原因就是如下
只要我们想启动自用的Converter那我们就要自定义。
(4)自定义Converter
以后我们要记住,想要定制WEBMvc的功能,就在实现了WebMvcConfiguration的类里面扩展一些功能即可。
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
}
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
}
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
}
@Override
public void addFormatters(FormatterRegistry registry) {
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
}
@Override
public void addCorsMappings(CorsRegistry registry) {
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
}
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
}
@Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
}
/* @Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
}*/
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
}
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
}
@Override
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
}
@Override
public Validator getValidator() {
return null;
}
@Override
public MessageCodesResolver getMessageCodesResolver() {
return null;
}
};
}
这里只能进行postman测试,原因和解决办法如下
(5)postman与浏览器协商完全匹配
-
它浏览器默认就两种:xml和JSON当你写自己的format=x-car就会错误
这些策略都已经添加好了,我们就只能自己指定了。 -
自定义SpringMVC
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new zidingyiMessageConverter());
}
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
//参数支持的媒体类型
Map<String,MediaType> mediaTypeMap = new HashMap<>();
mediaTypeMap.put("json",MediaType.APPLICATION_JSON);
mediaTypeMap.put("xml",MediaType.APPLICATION_ATOM_XML);
mediaTypeMap.put("car",MediaType.parseMediaType("application/x-car"));
ParameterContentNegotiationStrategy parameter = new ParameterContentNegotiationStrategy(mediaTypeMap);
configurer.strategies(Arrays.asList(parameter));
}
};
}
-
此时就可以:http://localhost:8888/car?format=car进行测试了
-
我们还可以这样设置
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new zidingyiMessageConverter());
}
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
//参数支持的媒体类型
Map<String,MediaType> mediaTypeMap = new HashMap<>();
mediaTypeMap.put("json",MediaType.APPLICATION_JSON);
mediaTypeMap.put("xml",MediaType.APPLICATION_ATOM_XML);
mediaTypeMap.put("car",MediaType.parseMediaType("application/x-car"));
//设置如下的两种的匹配模式
ParameterContentNegotiationStrategy parameter = new ParameterContentNegotiationStrategy(mediaTypeMap);
HeaderContentNegotiationStrategy headerStrategy = new HeaderContentNegotiationStrategy();
configurer.strategies(Arrays.asList(parameter,headerStrategy));
}
};
}
有可能我们添加的自定义的功能会覆盖默认很多功能,导致一些默认的功能失效。
大家考虑,上述功能除了我们完全自定义外?SpringBoot有没有为我们提供基于配置文件的快速修改媒体类型功能?怎么配置呢?【提示:参照SpringBoot官方文档web开发内容协商章节】