SpringBoot-Web开发之内容协商

  • 根据客户端接收能力不同,返回不同媒体类型的数据

默认协商规则

基于请求头内容协商:(默认开启)
  • 客户端向服务端发送请求,携带HTTP标准的Accept请求头,例如:application/json 、 text/xml 、 text/yaml
  • 服务端根据客户端请求头期望的数据类型进⾏动态返回
基于请求参数内容协商:(需要开启)
  • 发送请求 GET /projects/spring-boot?format=json
  • 匹配到 @GetMapping("/projects/spring-boot")
  • 根据参数协商,优先返回 json 类型数据【需要开启参数匹配设置】
  • 发送请求 GET /projects/spring-boot?format=xml,优先返回 xml 类型数据
bash 复制代码
# 开启基于请求参数的内容协商功能。 默认此功能不开启
spring.mvc.contentnegotiation.favor-parameter=true
# 指定内容协商时请求路径里使⽤的参数名。默认是 format
spring.mvc.contentnegotiation.parameter-name=type
bash 复制代码
http://localhost:8080/test/person?format=json 
http://localhost:8080/test/person?format=xml

内容协商原理

  • 判断当前响应头中是否已经有确定的媒体类型。MediaType
  • 获取客户端Accept请求头字段,判断支持接收的内容类型
    • contentNegotiationManager类,内容协商管理器,默认使用基于请求头的策略
    • HeaderContentNegotiationStrategy 确定客户端可以接收的内容类型
  • 遍历循环所有当前系统的 MessageConverter,看谁支持操作返回对象
  • 找到支持操作返回格式的converter,把converter支持的媒体类型统计出来
  • 比如json、xml等等,进行内容协商的最佳匹配媒体类型
  • 用支持将对象转为最佳匹配媒体类型的converter进行转化

自定义MessageConverter

  1. MessageConverter作用
  • 使用注解@ResponseBody的Controller方法响应数据出去,通过调用 RequestResponseBodyMethodProcessor 处理
  • Processor 处理方法通过 MessageConverter 处理返回值
  • 所有 MessageConverter 合起来可以支持各种媒体类型数据的操作(读、写)
  • 内容协商找到最终的 messageConverter
  1. 自定义MessageConverter
  • 配置类中添加一个 WebMvcConfigurer
java 复制代码
@Bean
public WebMvcConfigurer webMvcConfigurer(){
	return new WebMvcConfigurer() {
		@Override
		public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
			//加入自定义的Converter
			converters.add(new MyMessageConverter());
		}
	}
}
java 复制代码
public class MyMessageConverter implements HttpMessageConverter<Person> {

    @Override
    public boolean canRead(Class<?> clazz, MediaType mediaType) {
        
        return false;
    }

    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        //匹配是否是person类型
        return clazz.isAssignableFrom(Person.class);
    }

    /**
     * 服务器要统计所有MessageConverter都能写出哪些内容类型
     *
     * application/x-my
     * @return
     */
    @Override
    public List<MediaType> getSupportedMediaTypes() {
        
        return MediaType.parseMediaTypes("application/x-my");
    }

    @Override
    public Person read(Class<? extends Person> clazz, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException {
        return null;
    }

    @Override
    public void write(Person person, MediaType contentType, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {
        //自定义协议数据的写出格式
        String data = person.getUserName()+";"+person.getAge()+";"+person.getBirth();
        //写出去
        OutputStream body = outputMessage.getBody();
        body.write(data.getBytes());
    }
}
  • 自定义后效果
  • 此时的配置只支持匹配请求头是application/x-my,不支持匹配请求参数format=my
  1. 适配请求参数format=自定义
  • 配置类实现:配置类中WebMvcConfigurer重写configureContentNegotiation()
    • 第一种重写方法:覆盖默认协商策略strategies和媒体类型mediaTypes,全自定义
    • 第二种重写方法:只是追加媒体类型mediaTypes
java 复制代码
/**
 * 自定义内容协商策略
 * @param configurer
 */
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
	Map<String, MediaType> mediaTypes = new HashMap<>();
	mediaTypes.put("json",MediaType.APPLICATION_JSON);
	mediaTypes.put("xml",MediaType.APPLICATION_XML);
	mediaTypes.put("my",MediaType.parseMediaType("application/x-my"));
	//指定支持解析哪些参数对应的哪些媒体类型
	//配置匹配参数format的策略
	ParameterContentNegotiationStrategy parameterStrategy = 
                new ParameterContentNegotiationStrategy(mediaTypes);

	//自定义匹配的参数,默认为format
	//parameterStrategy.setParameterName("ff");

	//配置匹配请求头策略,否则就是匹配*/*
	HeaderContentNegotiationStrategy headerStrategy = 
                new HeaderContentNegotiationStrategy();

	//此处自定义后将覆盖默认的strategies
	configurer.strategies(Arrays.asList(parameterStrategy,headerStrategy));
}
java 复制代码
/**
 * 自定义内容协商策略
 * @param configurer
 */
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
    //添加自定义的媒体类型
	configurer.mediaType("my",MediaType.parseMediaType("application/x-my"));
}
  • 注解实现
bash 复制代码
spring:
  mvc:
    hiddenmethod:
      filter:
        enabled: true   #restFul的过滤器
    contentnegotiation:
      favor-parameter: true   #开启支持请求参数进行内容协商
      media-types:
        my: application/x-my  #自定义内容协商的媒体类型
相关推荐
weixin_460443562 小时前
2026年新修订《网络安全法》实施:Java私有化考试系统如何做好SBOM、漏洞治理与安全升级?
java·开发语言·在线考试系统·企业培训考试系统·宏远培训考试系统·国企培训考试系统·国产化培训系统
海鸥-w5 小时前
什么是AOP
java
Cc.Y5 小时前
Java零基础入门:可变字符串与包装类:StringBuilder、StringBuffer 与 通讯录管理系统实战
java·开发语言·python
paopaokaka_luck5 小时前
基于springboot+vue的智慧家政管理系统(AI问答、 WebSocket实时沟通、ECharts运营分析、订单规则与服务追踪)
java·javascript·spring boot·数据分析·echarts·mybatis
sm_926787055 小时前
RFID 标签打印的技术实现要点与二次开发实践
java·大数据·前端·c++·编辑器
阿森_dev5 小时前
2027 届毕设选题怎么选:30 个 Java 方向选题的实现难度排序
java·开发语言·spring boot·毕业设计·课程设计
Sarvartha6 小时前
Lambda 表达式和函数式接口
java·开发语言
计算机毕业设计杰瑞6 小时前
【计算机毕设精品】基于SpringBoot的一体化教务与学业质量管理系统的设计与实现,附源码_增删改查_java项目_可完美运行部署_网站项目
java·spring boot·课程设计
colinu6 小时前
C++ 异步任务与线程生命周期:`std::async`、`future`、`thread`、`jthread` 与协作式退出
java·jvm·c++
蜗牛互联网6 小时前
Python + JSON Schema 实现工单结构化输出与本地复核
java·人工智能·后端