「SpringBoot3 Web开发」内容协商

一套系统适配多端数据返回

image.png

1. 多端内容适配

1. 默认规则

SpringBoot 多端内容适配

  1. 基于请求头内容协商:(默认开启)
    1. 客户端向服务端发送请求,携带HTTP标准的Accept请求头
      1. Accept: application/jsontext/xmltext/yaml
      2. 服务端根据客户端请求头期望的数据类型进行动态返回
  2. 基于请求参数内容协商:(需要开启)
    1. 发送请求 GET /projects/spring-boot?format=json
    2. 匹配到 @GetMapping(“/projects/spring-boot”)
    3. 根据参数协商,优先返回 json 类型数据【需要开启参数匹配设置
    4. 发送请求 GET /projects/spring-boot?format=xml,优先返回 xml 类型数据

2. 效果演示

请求同一个接口,可以返回json和xml不同格式数据
web默认返回json格式

  1. 引入支持写出xml内容依赖
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
</dependency>
  1. 标注注解
@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;
    }
}

  1. 开启基于请求参数的内容协商
# 开启基于请求参数的内容协商功能。 默认参数名:format。 默认此功能不开启
spring.mvc.contentnegotiation.favor-parameter=true
# 指定内容协商时使用的参数名。默认是 format
spring.mvc.contentnegotiation.parameter-name=type
  1. 效果

image.pngimage.png

3. 配置协商规则与支持类型

  1. 修改内容协商方式
#使用参数进行内容协商
spring.mvc.contentnegotiation.favor-parameter=true  
#自定义参数名,默认为format
spring.mvc.contentnegotiation.parameter-name=myparam 
  1. 大多数 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. @ResponseBodyHttpMessageConverter处理

标注了@ResponseBody的返回值 将会由支持它的 HttpMessageConverter写给浏览器

  1. 如果controller方法的返回值标注了 @ResponseBody 注解
    1. 请求进来先来到DispatcherServletdoDispatch()进行处理
    2. 找到一个 HandlerAdapter 适配器。利用适配器执行目标方法
    3. RequestMappingHandlerAdapter来执行,调用invokeHandlerMethod()来执行目标方法
    4. 目标方法执行之前,准备好两个东西
      1. HandlerMethodArgumentResolver:参数解析器,确定目标方法每个参数值
      2. HandlerMethodReturnValueHandler:返回值处理器,确定目标方法的返回值改怎么处理
    5. RequestMappingHandlerAdapter 里面的invokeAndHandle()真正执行目标方法
    6. 目标方法执行完成,会返回返回值对象
    7. 找到一个合适的返回值处理器 HandlerMethodReturnValueHandler
    8. 最终找到 RequestResponseBodyMethodProcessor能处理 标注了 @ResponseBody注解的方法
    9. RequestResponseBodyMethodProcessor 调用writeWithMessageConverters ,利用MessageConverter把返回值写出去

上面解释:@ResponseBodyHttpMessageConverter处理

  1. HttpMessageConverter先进行内容协商
    1. 遍历所有的MessageConverter看谁支持这种内容类型的数据
    2. 默认MessageConverter有以下
      image.png
  2. 最终因为要json所以MappingJackson2HttpMessageConverter支持写出json
  3. jackson用ObjectMapper把对象写出去

2. WebMvcAutoConfiguration提供几种默认HttpMessageConverters

  • EnableWebMvcConfiguration通过 addDefaultHttpMessageConverters添加了默认的MessageConverter;如下:
    • ByteArrayHttpMessageConverter: 支持字节数据读写
    • StringHttpMessageConverter: 支持字符串读写
    • ResourceHttpMessageConverter:支持资源读写
    • ResourceRegionHttpMessageConverter: 支持分区资源写出
    • AllEncompassingFormHttpMessageConverter:支持表单xml/json读写
    • MappingJackson2HttpMessageConverter: 支持请求响应体Json读写

默认8个:
image.png

系统提供默认的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
	);
  • 16
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值