SpringBoot之内容协商

现象演示

假设有一个需求是根据终端的不同,返回不同形式的数据,比如 PC 端需要以 HTML 格式返回数据,APP、小程序端需要以 JSON 格式返回数据。这时我们是 coding 几个相似的接口?还是在一个接口里面做复杂判断处理?两个方案貌似都不理想,一旦需求改动,维护的东西就比较多,这时候我们利用 SpringBoot 的内容协商功能,就可以很好的简化逻辑,案例演示如下:

创建实体类Dog

public class Dog {private String name;public Dog() {}public Dog(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Dog{" +"name='" + name + '\'' +'}';}
}

创建Controller

@RestController
@RequestMapping("/content_negotiation")
public class ContentNegotiationController {@GetMapping("/simple")public Dog getDog() {return new Dog("wangcai");}
}

开启参数形式内容协商

spring:mvc:contentnegotiation:favor-parameter: true

添加POM依赖 (让 SpringBoot 有返回相关格式数据的能力) 

<dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-xml</artifactId><version>2.16.1</version>
</dependency>

请求及响应

返回 JSON 格式的数据

返回 HTML 格式的数据

源码解析

HandlerMethodReturnValueHandlerComposite#handleReturnValue

总体分为两步:

  1. 选择一个 HandlerMethodReturnValueHandler 来处理当前返回值
  2. 处理返回值

选择 HandlerMethodReturnValueHandler

SpringBoot 会手动注册的一些 HandlerMethodReturnValueHandler ,一共有15 个 (SpringBoot 版本 2.6.13),我们主要关注 RequestResponseBodyMethodProcessor 这个handler。

RequestResponseBodyMethodProcessor#supportsReturnType

如果接口方法含有 @ResponseBody 注解,或者相关Controller上含有 @ResponseBody 注解,则RequestResponseBodyMethodProcessor 都可以处理

处理返回值

writeWithMessageConverters 方法大概有以下几个步骤:

  1. 获取 acceptableTypes
  2. 获取 producibleTypes
  3. 获取 mediaTypesToUse
  4. 给 mediaTypesToUse 排序
  5. 获取 selectedMediaType
  6. 写出数据
获取 acceptableTypes

分为两个分支:

  • Response 是否指定 Content-Type,并且 Content-Type 的类型不是 */*,则直接跳转到步骤6 (写出数据)
  • 获取 acceptableTypes
AbstractMessageConverterMethodProcessor#getAcceptableMediaTypes

默认情况下,只有 HeaderContentNegotiationStrategy ,因为我们在现象演示的时候,将属性 spring.mvc.contentnegotiation.favor-parameter 设置为 true,所以多出来一个 ParameterContentNegotiationStrategy 。如果 ParameterContentNegotiationStrategy 的 resolveMediaTypes 方法的返回值不为 null 且不为 MEDIA_TYPE_ALL_LIST,则以 ParameterContentNegotiationStrategy 的 resolveMediaTypes 方法返回值为准,即 ParameterContentNegotiationStrategy 的优先级高于 HeaderContentNegotiationStrategy

ParameterContentNegotiationStrategy#resolveMediaTypes
getMediaTypeKey

默认情况下,parameterName 的值为 format

修改 parameterName 默认值
spring:mvc:contentnegotiation:favor-parameter: trueparameter-name: custom_format

resolveMediaTypeKey

就是以 parameterName 对应的属性值为 key, 从 URL 中获取 value,再以该 value 为 mediaTypeKey 从一个 map (mediaTypes)中获取 MediaType

mediaTypes的初始赋值

WebMvcConfigurationSupport#mvcContentNegotiationManager

因为我们在现象演示的时候添加了相关POM依赖,所有 mediaTypes 的 内容如下所示:

即默认情况下,format 的参数值含义如下 :

  • json:acceptableTypes 为 [ application/json ]
  • xml:acceptableTypes 为 [ application/xml ]
  • 其他:acceptableTypes 为 [ */*]

也可以自定义key,配置如下所示,这时候如果参数携带 custom_format=lanyu,系统也会返回 application/xml 格式的数据

spring:mvc:contentnegotiation:favor-parameter: trueparameter-name: custom_formatmedia-types: {lanyu : application/xml}

HeaderContentNegotiationStrategy#resolveMediaTypes

HeaderContentNegotiationStrategy 的 resolveMediaTypes 方法比较简单,就是获取请求头中 Accept 的值

获取 producibleTypes

大概分为以下几种情况

  • Request 域的 HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE 是否为 null
    • 属性值不为 null:返回指定的 mediaTypes
    • 属性值为 null
      • 是否存在 messageConverters 的 canWrite 方法返回 true
        • 存在:相关 messageConverters 的 getSupportedMediaTypes 方法返回值的集合
        • 不存在:MediaType.ALL
获取 mediaTypesToUse

主要通过 isCompatibleWith 方法判断  acceptableType 和 producibleType 是不是兼容的,主要有以下几种情况 :

  • producibleType 为  null:返回false
  • producibleType 不为null
    • acceptableType 为 */* 或  producibleType为 */* :返回true
    • acceptableType 和 producibleType 都不为 */*
      • acceptableType 和 producibleType 的 type 一致
        • acceptableType 和 producibleType 的 subtype 一致 : 返回true
        • acceptableType 和 producibleType 的 subtype 不一致
          • acceptableType 或 producibleType 的 subtype 的 isWildcardSubtype 方法返回true
            • acceptableType 或 producibleType 的 subtype 为 *:返回true
            • acceptableType 的 subtype 以 *+ 开头,并且 acceptableType 的后缀与producibleType一致:返回true
            • producibleType 的 subtype 以 *+ 开头,并且 producibleType 的后缀与acceptableType 一致:返回true
            • 其他情况:返回false
          • acceptableType 与 producibleType 的 subtype 的 isWildcardSubtype 方法都返回false:返回false
      • acceptableType 和 producibleType 的 type 不一致:返回false
给 mediaTypesToUse 排序

排序规则1:

  1. 权重越大优先级越高
  2. 参数个数越多优先级越高

如果排序规则1未判断出谁的优先级高,则使用排序规则2,排序规则2如下:

  1. 权重越大优先级越高
  2. type类型不为 *
  3. subtype类型不为 * 或以 *+ 开头
  4. 参数个数越多优先级越高
获取 selectedMediaType

遍历上一步经过排序的 mediaTypes,如果存在一个 mediaType 满足以下条件,则直接返回

  1. type 类型不为 * ,subtype 类型不为 * 且不以 *+ 开头
  2. MediaType 为 */* 或 application/*

写出数据

如果存在一个 HttpMessageConverter 的 canWrite 方法返回 true,则使用 HttpMessageConverter 的 write 方法写出数据

扩展:自定义HttpMessageConverter处理自定义协议

创建实体类Cat

public class Cat {private String name;public Cat() {}public Cat(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Cat{" +"name='" + name + '\'' +'}';}
}

创建自定义HttpMessageConverter

public class LanyuHttpMessageConverter implements HttpMessageConverter {@Overridepublic boolean canRead(Class clazz, MediaType mediaType) {return false;}@Overridepublic boolean canWrite(Class clazz, MediaType mediaType) {if (Cat.class == clazz) {return true;}return false;}@Overridepublic List<MediaType> getSupportedMediaTypes() {return Collections.singletonList(MediaType.parseMediaType("lanyu/custom"));}@Overridepublic Object read(Class clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {return null;}@Overridepublic void write(Object o, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {try {Cat cat = (Cat) o;String data = "cat = {name : " + cat.getName() + "}";OutputStream outputStream = outputMessage.getBody();outputStream.write(data.getBytes());outputStream.flush();} catch (Exception e) {throw new RuntimeException(e);}}
}

创建配置类

@Configuration
public class MessageConfig implements WebMvcConfigurer {@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) {converters.add(new LanyuHttpMessageConverter());}
}

接口及响应

@GetMapping("/custom_protocol")
public Cat getCustomProtocolData() {return new Cat("tom");
}

我们也可以让我们自定义的协议支持 URL 传参形式,配置如下

spring:mvc:contentnegotiation:favor-parameter: trueparameter-name: custom_formatmedia-types: {lanyu : lanyu/custom}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/864837.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SQL执行慢排查以及优化思路

数据库服务器的优化步骤 当我们遇到数据库调优问题的时候&#xff0c;该如何思考呢&#xff1f;我把思考的流程整理成了下面这张图。 整个流程划分成了观察&#xff08;Show status&#xff09;和行动&#xff08;Action&#xff09;两个部分。字母 S 的部分代表观察&#xf…

LSH算法:高效相似性搜索的原理与Python实现I

局部敏感哈希&#xff08;LSH&#xff09;技术是快速近似最近邻&#xff08;ANN&#xff09;搜索中的一个关键方法&#xff0c;广泛应用于实现高效且准确的相似性搜索。这项技术对于许多全球知名的大型科技公司来说是不可或缺的&#xff0c;包括谷歌、Netflix、亚马逊、Spotify…

分文件编译(简单学生系统)

定义学生基本信息 ①输出所有学生信息 ②删除某个学生后&#xff0c;输出所有学生信息 ③修改某个学生信息后&#xff0c;输出所有学生信息 ④查找某个学生的信息 main.c #include"k11*.h" int main(int argc, const char *argv[]) {struct student p[4]{{"…

Echarts-柱状图

1.案例1 1.1代码 option = {textStyle: {color: #fff // 标题文字颜色为白色},tooltip: {trigger: axis,axisPointer: {type: shadow,},},legend: {textStyle: {color: white}},grid: {top: 15%,left: 4%,right: 4%,bottom: 7%,containLabel: true},xAxis:{type: category,da…

仿全民飞机大战射击网页游戏源码

仿全民飞机大战设计网页游戏源码&#xff0c;画质精美的飞机大战手机端游戏源码 微信扫一扫免费下载源码

Mac|install vue

安装Node&#xff1a;Node.js — Download Node.js 选择系统为mac&#xff0c;安装步骤在终端输入 &#xff08;放文字版在这里&#xff5e;方便复制&#xff09; # installs nvm (Node Version Manager) curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/ins…

78.Vue 3 重用性模态框组件

模态框是大多数 Web 应用程序中的基本构建块。虽然最初实现起来可能看起来有点棘手&#xff0c;但实际上&#xff0c;使用 Vue 和一些 Flexbox 技巧&#xff0c;这不仅可行&#xff0c;而且非常简单。 让我们一起实现一个基础的模态框组件。 架构如下&#xff1a; AppModal.vue…

docker-compose搭建prometheus、grafana

一、安装prometheus 1、安装 version: 3.1services:prometheus:image: prom/prometheus:v2.48.0container_name: prometheushostname: prometheusrestart: alwaysvolumes:- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml- ./prometheus/:/etc/prometheus/port…

第二篇——始计篇:“计”是最早的SWOT分析

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 第二次详读孙子兵法&#xff0c;当初听讲解的时候&#xff0c;就觉得自己…

WPF UI 3D 多轴 机械臂 stl 模型UI交互

鼠标交互&#xff08;没有强调场景的变换&#xff09; 鼠标命中测试&#xff08;HitTest 不推荐&#xff09; 平面对象加载 数据绑定&#xff08;数据与动作&#xff09; 环境配置与相关方法 模型准备&#xff1a;Blender/SolidWorks 模型导入 HelixToolkit更多案例…

驱动LSM6DS3TR-C实现高效运动检测与数据采集(6)----FIFO数据读取与配置

驱动LSM6DS3TR-C实现高效运动检测与数据采集.6--FIFO数据读取与配置 概述视频教学样品申请源码下载主要内容生成STM32CUBEMX串口配置IIC配置CS和SA0设置ICASHE修改堆栈串口重定向参考驱动程序FIFO参考程序初始化管脚获取ID复位操作设置量程BDU设置设置速率FIFO读取程序设置FIFO…

无忧易售升级:产品视频翻译支持,拓宽全球市场边界

在电商内容营销迈入视频时代的今天&#xff0c;无忧易售ERP推出针对OZON、Wish、TikTok、Wildberries&#xff08;野莓&#xff09;四大平台的产品视频翻译功能&#xff0c;彻底打破语言壁垒&#xff0c;让全球卖家的商品故事&#xff0c;以更生动、更直观的方式&#xff0c;触…

币界网讯,币安准备与SEC 展开长期法律对决

刚刚&#xff0c;数字货币交易所的领头羊Binance公布了法律策略&#xff0c;未来将会采取大胆举措与美国证券交易委员会 (SEC) 展开长期法律斗争&#xff0c;彰显其对监管合规的承诺。小编认为&#xff0c;Binance的这一战略立场是向美国SEC传递的道歉信&#xff0c;自从美国SE…

老机福音!最精简最快的Win7系统:免费下载!

有些老旧计算机因为配置较低&#xff0c;所以运作起来比较卡顿。有用户想知道安装哪些系统能提升老旧计算机的运行速度&#xff1f;系统之家小编推荐安装以下最精简最快的Win7系统版本&#xff0c;优化老旧计算机的性能&#xff0c;确保安装后时刻运作流畅稳定。 推荐一&#x…

8619 公约公倍

这个问题可以通过计算最大公约数 (GCD) 和最小公倍数 (LCM) 来解决。我们需要找到一个整数&#xff0c;它是 a, b, c 的 GCD 的倍数&#xff0c;同时也是 d, e, f 的 LCM 的约数。 以下是解决这个问题的步骤&#xff1a; 1. 计算 a, b, c 的最大公约数。 2. 计算 d, e, f 的最…

Java语法系列 小白入门参考资料 方法

方法的概念及使用 方法概念 方法出现的原因 在编程中&#xff0c;某段功能的代码可能频繁使用到&#xff0c;如果在每个位置都重新实现一遍&#xff0c;会&#xff1a; 1. 使程序变得繁琐 2. 开发效率低下&#xff0c;做了大量重复性的工作 3. 不利于维护&#xff0c;需要…

115V 400HZ远机位电源车在国际机场的推广与应用

随着我国航空业的快速发展&#xff0c;对于远机位电源车的需求也越来越迫切。远机位电源车可以为飞机提供稳定、可靠的电力&#xff0c;确保飞机在停机、起降、航行等环节中正常运行。在当前的航空技术中&#xff0c;115V 400HZ 远机位电源车技术发展及其在航空领域的应用逐年增…

maketrans()方法——创建字符映射的转换表

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 语法参考 maketrans()方法用于创建字符映射的转换表&#xff0c;对于接受两个参数的最简单的调用方式&#xff0c;第一个参数是字符串&#xff0c;表…

炎黄数智人:招商局集团推出AI数字员工“招小影”

引言 在全球数字化浪潮的推动下&#xff0c;招商局集团开启了一项具有里程碑意义的项目。招商局集团将引入AI数字员工“招小影”&#xff0c;这一举措不仅彰显了招商局集团在智能化转型方面的坚定决心&#xff0c;也为企业管理模式的创新注入了新的活力。 “招小影”是一款集成…

一文了解什么是车载Tbox

目录 前言一、Tbox是什么呢?二、Tbox架构三、App——TSP——Tbox交互时序图四、汽车混合网关拓扑结构示例五、Tbox功能 前言 强烈建议提前阅读一下这篇文章&#xff1a;车机Tbox介绍 一、Tbox是什么呢? Tbox是汽车上的一个盒子&#xff0c;指的是Telematics BOX&#xff0c…