一套系统适配多端数据返回
1. 多端内容适配
1. 默认规则
SpringBoot 多端内容适配。
- 基于请求头内容协商:(默认开启)
- 客户端向服务端发送请求,携带HTTP标准的Accept请求头。
- Accept:
application/json
、text/xml
、text/yaml
- 服务端根据客户端请求头期望的数据类型进行动态返回
- Accept:
- 客户端向服务端发送请求,携带HTTP标准的Accept请求头。
- 基于请求参数内容协商:(需要开启)
- 发送请求 GET /projects/spring-boot?format=json
- 匹配到 @GetMapping(“/projects/spring-boot”)
- 根据参数协商,优先返回 json 类型数据【需要开启参数匹配设置】
- 发送请求 GET /projects/spring-boot?format=xml,优先返回 xml 类型数据
2. 效果演示
请求同一个接口,可以返回json和xml不同格式数据
web默认返回json格式
- 引入支持写出xml内容依赖
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
- 标注注解
@JacksonXmlRootElement // 可以写出为xml文档
@Data
public class Person {
private Long id;
private String userName;
private String email;
private Integer age;
}
@RestController
public class PersonController {
// 默认支持把对象json格式,因为默认web场景导入了 jsckson的包
// 也支持讲数据转为xml 需要导入相关的xml包
@GetMapping("/person")
public Person person() {
Person person = new Person();
person.setId(1L);
person.setUserName("张三");
person.setAge(15);
person.setEmail("JJ");
return person;
}
}
- 开启基于请求参数的内容协商
# 开启基于请求参数的内容协商功能。 默认参数名:format。 默认此功能不开启
spring.mvc.contentnegotiation.favor-parameter=true
# 指定内容协商时使用的参数名。默认是 format
spring.mvc.contentnegotiation.parameter-name=type
- 效果
3. 配置协商规则与支持类型
- 修改内容协商方式
#使用参数进行内容协商
spring.mvc.contentnegotiation.favor-parameter=true
#自定义参数名,默认为format
spring.mvc.contentnegotiation.parameter-name=myparam
- 大多数 MediaType 都是开箱即用的。也可以自定义内容类型,如:
spring.mvc.contentnegotiation.media-types.yaml=text/yaml
2. 自定义内容返回
1. 增加yaml返回支持
导入依赖
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
</dependency>
把对象写出成YAML
下方是在
Controller
中进行编写测试的,后期说明测试
public static void main(String[] args) throws JsonProcessingException {
Person person = new Person();
person.setId(1L);
person.setUserName("张三");
person.setEmail("aaa@qq.com");
person.setAge(18);
YAMLFactory factory = new YAMLFactory()
.disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER);
ObjectMapper mapper = new ObjectMapper(factory);
String s = mapper.writeValueAsString(person);
System.out.println(s);
}
2. 编写配置
#新增一种媒体类型
spring.mvc.contentnegotiation.media-types.yaml=text/yaml
增加HttpMessageConverter
组件,专门负责把对象写出为yaml格式
package com.example.Conf;
import com.example.component.MyYamlHttpMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
@Configuration
public class MyConfig implements WebMvcConfigurer {
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override // 配置一个能把对象转为yaml的messageConverter
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MyYamlHttpMessageConverter());
}
};
}
}
3. 思考:如何增加其他
- 配置媒体类型支持:
spring.mvc.contentnegotiation.media-types.yaml=text/yaml
- 编写对应的
HttpMessageConverter
,要告诉Boot这个支持的媒体类型- 按照3的示例
- 把MessageConverter组件加入到底层
- 容器中放一个
WebMvcConfigurer
组件,并配置底层的MessageConverter
- 容器中放一个
3. HttpMessageConverter的示例写法
package com.example.component;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
public class MyYamlHttpMessageConverter extends AbstractHttpMessageConverter<Object> {
private ObjectMapper objectMapper = null; // 把对象转为yaml
public MyYamlHttpMessageConverter() {
// 媒体类型
// 告诉SpringBoot这个MessageConverter支持哪种媒体类型
super(new MediaType("text", "yaml", Charset.forName("UTF-8")));
YAMLFactory factory = new YAMLFactory().disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER);
this.objectMapper = new ObjectMapper(factory);
}
@Override
protected boolean supports(Class<?> clazz) {
// 只要是对象类型 都支持 ;不是基本类型
return true;
}
@Override // @RequestBody 接受请求体
protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return null;
}
@Override // @ResponseBody 响应体 把对象写出去
protected void writeInternal(Object methodReturnValue, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
/* OutputStream body = outputMessage.getBody();
try{
this.objectMapper.writeValue(body,methodReturnValue);
}finally {
body.close();
}*/
/*上方的额简写 try-with写法,自动关流*/
try (OutputStream body = outputMessage.getBody()) {
this.objectMapper.writeValue(body, methodReturnValue);
}
}
}
3. 内容协商原理- HttpMessageConverter
HttpMessageConverter
怎么工作?何时工作?- 定制
HttpMessageConverter
来实现多端内容协商- 编写
WebMvcConfigurer
提供的configureMessageConverters
底层,修改底层的MessageConverter
1. @ResponseBody
由HttpMessageConverter
处理
标注了
@ResponseBody
的返回值 将会由支持它的HttpMessageConverter
写给浏览器
- 如果controller方法的返回值标注了
@ResponseBody
注解- 请求进来先来到
DispatcherServlet
的doDispatch()
进行处理 - 找到一个
HandlerAdapter
适配器。利用适配器执行目标方法 RequestMappingHandlerAdapter
来执行,调用invokeHandlerMethod()
来执行目标方法- 目标方法执行之前,准备好两个东西
HandlerMethodArgumentResolver
:参数解析器,确定目标方法每个参数值HandlerMethodReturnValueHandler
:返回值处理器,确定目标方法的返回值改怎么处理
RequestMappingHandlerAdapter
里面的invokeAndHandle()
真正执行目标方法- 目标方法执行完成,会返回返回值对象
- 找到一个合适的返回值处理器
HandlerMethodReturnValueHandler
- 最终找到
RequestResponseBodyMethodProcessor
能处理 标注了@ResponseBody
注解的方法 RequestResponseBodyMethodProcessor
调用writeWithMessageConverters
,利用MessageConverter
把返回值写出去
- 请求进来先来到
上面解释:
@ResponseBody
由HttpMessageConverter
处理
HttpMessageConverter
会先进行内容协商- 遍历所有的
MessageConverter
看谁支持这种内容类型的数据 - 默认
MessageConverter
有以下
- 遍历所有的
- 最终因为要
json
所以MappingJackson2HttpMessageConverter
支持写出json - jackson用
ObjectMapper
把对象写出去
2. WebMvcAutoConfiguration
提供几种默认HttpMessageConverters
EnableWebMvcConfiguration
通过addDefaultHttpMessageConverters
添加了默认的MessageConverter
;如下:ByteArrayHttpMessageConverter
: 支持字节数据读写StringHttpMessageConverter
: 支持字符串读写ResourceHttpMessageConverter
:支持资源读写ResourceRegionHttpMessageConverter
: 支持分区资源写出AllEncompassingFormHttpMessageConverter
:支持表单xml/json读写MappingJackson2HttpMessageConverter
: 支持请求响应体Json读写
默认8个:
系统提供默认的MessageConverter 功能有限,仅用于json或者普通返回数据。额外增加新的内容协商功能,必须增加新的
HttpMessageConverter
3. WebMvcConfigurationSupport
提供了很多的默认设置。
判断系统中是否有相应的类:如果有,就加入相应的HttpMessageConverter
jackson2Present = ClassUtils.isPresent(
"com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator",
classLoader
);
jackson2XmlPresent = ClassUtils.isPresent(
"com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader
);
jackson2SmilePresent = ClassUtils.isPresent(
"com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader
);