前言
在实际应用开发场景中,我们有需求实现多端内容请求的适配,例如某些客户端需要返回json数据,有些客户端需要返回xml数据,有些客户端要返回yaml数据,这个时候就需要服务端做内容返回的适配,如果按照提供不同的接口去适配,就需要开发多套接口实现,如果有大量的接口需要适配,这种方案就明显不太适用。springboot内部也提供一套多端内容协商适配的方案。主要通过俩种方式实现,一种是基于请求头的内容适配,一种是基于请求参数的内容适配,下面我们针对这俩种方式,通过案例实现xml数据的多端内容协商适配。其底层原理是通过HttpMessageConverter实现内容协商适配。
正文
①导入jackson的xml依赖和yaml依赖
<!-- xml依赖--> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </dependency> <!-- yaml依赖--> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-yaml</artifactId> </dependency>
②在响应数据的实体上标注注解@JacksonXmlRootElement用于实现xml内容协商数据返回
- 用户对象实体类person
package com.yundi.isbc.entity; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import lombok.Data; import lombok.experimental.Accessors; import java.util.ArrayList; import java.util.List; @JacksonXmlRootElement @Accessors(chain = true) @Data public class Person { /** * id */ private String id; /** * 名称 */ private String name; /** * 年龄 */ private Integer age; /** * 手机号 */ private String phone; /** * 住址 */ private List<Address> addresses = new ArrayList<>(); }
- 地址对象Address
package com.yundi.isbc.entity; import lombok.Data; import lombok.experimental.Accessors; @Accessors(chain = true) @Data public class Address { /** * 地址名称 */ private String addressName; }
③实现一个http请求接口用于验证请求返回内容
package com.yundi.isbc.controller;
import com.yundi.isbc.entity.Address;
import com.yundi.isbc.entity.Person;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@RestController
@RequestMapping("/app")
public class AppController {
@GetMapping(value = "list")
public List<Person> list() {
List<Person> list = new ArrayList<>();
list.add(new Person()
.setId("1")
.setName("小明")
.setAge(18)
.setPhone("18288474612")
.setAddresses(Arrays.asList(new Address().setAddressName("北京市"))));
return list;
}
}
④方式一:通过请求头accept的参数配置实现json或者xml数据的访问
- json数据返回(accept=application/json)
- xml数据返回(accept=application/xml)
⑤方式二:通过请求参数实现json或者xml数据的访问
- 在application.yml配置文件中开启内容请求协商的配置,此处type是传参的名字,可根据实际需求自定义实现
server: port: 8080 spring: mvc: # 开启基于请求参数内容协商的功能 contentnegotiation: favor-parameter: true parameter-name: type
- 请求json数据
- 请求xml数据
⑥yaml数据的返回实现略有不同,先在application.yml文件中开启yaml内容协商数据的支持
server: port: 8080 spring: mvc: # 开启基于请求参数内容协商的功能 contentnegotiation: favor-parameter: true parameter-name: type # 开启yaml协商数据的支持 media-types: yaml: application/yaml
⑦xml本身实现了消息转换器,yaml这里我们需要自行实现其消息转换器HttpMessageConverter
,转换成我们想要的数据格式 ,MyYamlMessageConverter.class
- 实现代码
package com.yundi.isbc.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 MyYamlMessageConverter extends AbstractHttpMessageConverter<Object> { private ObjectMapper objectMapper = null; public MyYamlMessageConverter() { //消息是配置支持的数据类型及编码格式 super(new MediaType("application", "yaml", Charset.forName("utf-8"))); //通过YAMLFactory工厂初始化ObjectMapper对象 YAMLFactory yamlFactory = new YAMLFactory().disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER); this.objectMapper = new ObjectMapper(yamlFactory); } @Override protected boolean supports(Class<?> clazz) { //判断支持哪些数据类型,这里默认支持object对象 return true; } @Override protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { //请求参数的处理 return null; } @Override protected void writeInternal(Object obj, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { //响应数据的处理,这里使用jackson的工具ObjectMapper将java对象转换成yaml数据格式返回 try (OutputStream os = outputMessage.getBody()) { this.objectMapper.writeValue(os, obj); } } }
⑧在springmvc中配置中自定义的yaml消息转换器 MyYamlMessageConverter
- 实现代码
package com.yundi.isbc.config; import com.yundi.isbc.component.MyYamlMessageConverter; 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 MyMvcConfig { @Bean public WebMvcConfigurer webMvcConfigurer() { return new WebMvcConfigurer() { //配置一个能把对象转换为yaml格式的MessageConverter转换器 @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(new MyYamlMessageConverter()); } }; } }
⑨访问yaml格式数据
方式一:通过请求头访问
方式二:通过请求参数访问
结语
本节内容到这里就结束了,在实际的开发过程中,我们也可以根据实际情况返回其它类型的数据格式,关键的地方是我们需要实现MessageConverter消息转换器,通过对应类型的消息转换器实现对应类型消息内容的返回。好了,本节内容到这里就结束了,我们下期见。。。。。。