SpringBoot系列教程之定义接口返回类型的几种方式

news2024/12/24 22:03:37

本文节选自 《Spring WEB专栏》

WEB系列】 定义接口返回类型的几种方式

实现一个 web 接口返回 json 数据,基本上是每一个 javaer 非常熟悉的事情了;那么问题来了,如果我有一个接口,除了希望返回 json 格式的数据之外,若也希望可以返回 xml 格式数据可行么?

答案当然是可行的,接下来我们将介绍一下,一个接口的返回数据类型,可以怎么处理

I. 项目搭建

本文创建的实例工程采用SpringBoot 2.2.1.RELEASE + maven 3.5.3 + idea进行开发

1. pom 依赖

具体的 SpringBoot 项目工程创建就不赘述了,对于 pom 文件中,需要重点关注下面两个依赖类

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.dataformat</groupId>
        <artifactId>jackson-dataformat-xml</artifactId>
    </dependency>
</dependencies>

注意 jackson-datafromat-xml这个依赖,加上这个主要时为了支持返回 xml 格式的数据

II. 返回类型设置的多种方式

正常来讲,一个 RestController 的接口,默认返回的是 Json 格式数据,当我们引入了上面的 xml 包之后,会怎样呢?返回的还是 json 么?

1.通过 produce 设置返回类型

如果一个接口希望返回 json 或者 xml 格式的数据,最容易想到的方式就是直接设置RequestMapping注解中的 produce 属性

这个值主要就是用来设置这个接口响应头中的content-type; 如我们现在有两个接口,一个指定返回 json 格式数据,一个指定返回 xml 格式数据,可以如下写

@RestController
public class IndexRest {

    @Data
    public static class ResVo<T> {
        private int code;
        private String msg;
        private T data;

        public ResVo(int code, String msg, T data) {
            this.code = code;
            this.msg = msg;
            this.data = data;
        }
    }
    @GetMapping(path = "/xml", produces = {MediaType.APPLICATION_XML_VALUE})
    public ResVo<String> xml() {
        return new ResVo<>(0, "ok", "返回xml");
    }

    @GetMapping(path = "/json", produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResVo<String> json() {
        return new ResVo<>(0, "ok", "返回json");
    }
}

上面的实现中

  • xml 接口,指定produces = application/xml
  • json 接口,指定produces = applicatin/json

接下来我们访问一下看看返回的是否和预期一致

从上面截图也可以看出,xml 接口返回的是 xml 格式数据;json 接口返回的是 json 格式数据

2. 通过请求头 accept 设置返回类型

上面的方式,非常直观,自然我们就会有一个疑问,当接口上不指定 produces 属性时,直接访问会怎么表现呢?

@GetMapping(path = "/")
public ResVo<String> index() {
    return new ResVo<>(0, "ok", "简单的测试");
}

请注意上面的截图,两种访问方式返回的数据类型不一致

  • curl 请求:返回 json 格式数据
  • 浏览器请求:返回 application/xhtml+xml响应头的数据(实际上还是 xml 格式)

那么问题来了,为什么两者的表现形式不一致呢?

对着上面的图再看三秒,会发现主要的差别点就在于请求头Accept不同;我们可以通过这个请求头参数,来要求服务端返回我希望的数据类型

如指定返回 json 格式数据

curl 'http://127.0.0.1:8080' -H 'Accept:application/xml' -iv

curl 'http://127.0.0.1:8080' -H 'Accept:application/json' -iv

从上面的执行结果也可以看出,返回的类型与预期的一致;

说明

请求头可以设置多种 MediaType,用英文逗号分割,后端接口会根据自己定义的 produce 与请求头希望的 mediaType 取交集,至于最终选择的顺序则以 accept 中出现的顺序为准

看一下实际的表现来验证下上面的说法

通过请求头来控制返回数据类型的方式可以说是非常经典的策略了,(遵循 html 协议还有什么好说的呢!)

3. 请求参数来控制返回类型

除了上面介绍的两种方式之外,还可以考虑为所有的接口,增加一个根据特定的请求参数来控制返回的类型的方式

比如我们现在定义,所有的接口可以选传一个参数 mediaType,如果值为 xml,则返回 xml 格式数据;如果值为 json,则返回 json 格式数据

当不传时,默认返回 json 格式数据

基于此,我们主要借助 mvc 配置中的内容协商ContentNegotiationConfigurer来实现

@SpringBootApplication
public class Application implements WebMvcConfigurer {
    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.favorParameter(true)
                // 禁用accept协商方式,即不关心前端传的accept值
//                .ignoreAcceptHeader(true)
                // 哪个放在前面,哪个的优先级就高; 当上面这个accept未禁用时,若请求传的accept不能覆盖下面两种,则会出现406错误
                .defaultContentType(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)
                // 根据传参mediaType来决定返回样式
                .parameterName("mediaType")
                // 当acceptHeader未禁用时,accept的值与mediaType传参的值不一致时,以mediaType的传值为准
                // mediaType值可以不传,为空也行,但是不能是json/xml之外的其他值
                .mediaType("json", MediaType.APPLICATION_JSON)
                .mediaType("xml", MediaType.APPLICATION_XML);
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}

上面的实现中,添加了很多注释,先别急;我来逐一进行说明

.parameterName("mediaType")
// 当acceptHeader未禁用时,accept的值与mediaType传参的值不一致时,以mediaType的传值为准
// mediaType值可以不传,为空也行,但是不能是json/xml之外的其他值
.mediaType("json", MediaType.APPLICATION_JSON)
.mediaType("xml", MediaType.APPLICATION_XML);

上面这三行代码,主要就是说,现在可以根据传参 mediaType 来控制返回的类型,我们新增一个接口来验证一下

@GetMapping(path = "param")
public ResVo<String> params(@RequestParam(name = "mediaType", required = false) String mediaType) {
    return new ResVo<>(0, "ok", String.format("基于传参来决定返回类型:%s", mediaType));
}

我们来看下几个不同的传参表现

# 返回json格式数据
curl 'http://127.0.0.1:8080/param?mediaType=json' -iv
# 返回xml格式数据
curl 'http://127.0.0.1:8080/param?mediaType=xml' -iv
# 406错误
curl 'http://127.0.0.1:8080/param?mediaType=text' -iv
# 走默认的返回类型,json在前,所以返回json格式数据(如果将xml调整到前面,则返回xml格式数据,主要取决于 `.defaultContentType(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)`)
curl 'http://127.0.0.1:8080/param' -iv

疑问:若请求头中传递了 Accept 或者接口上定义了 produce,会怎样?

当指定了 accept 时,并且传参中指定了 mediaType,则以传参为准

  • accept: application/json,application.xml, 此时mediaType=json, 返回 json 格式
  • accept: application/json, 此时 mediaTyep=xml, 返回 xml 格式
  • accept: text/html,此时mediaType=xml ,此时返回的也是 xml 格式
  • accept: text/html,此时mediaType不传时 ,因为无法处理text/html类型,所以会出现 406
  • accept: application/xml, 但是mediaType不传,虽然默认优先是 json,此时返回的也是 xml 格式,与请求头希望的保持一致

但是若传参与 produce 冲突了,那么就直接 406 异常,不会选择 mediaType 设置的类型

  • produce = applicatin/json, 但是 mediaType=xml,此时就会喜提 406

细心的小伙伴可能发现了上面的配置中,注释了一行 .ignoreAcceptHeader(true),当我们把它打开之后,前面说的 Accept 请求头可以随意传,我们完全不 care,当做没有传这个参数进行处理即可开

4.小结

本文介绍了三种方式,控制接口返回数据类型

方式一

接口上定义 produce, 如 @GetMapping(path = "p2", produces = {"application/xml", "application/json"})

注意 produces 属性值是有序的,即先定义的优先级更高;当一个请求可以同时接受 xml/json 格式数据时,上面这个定义会确保这个接口现有返回 xml 格式数据

方式二

借助标准的请求头 accept,控制希望返回的数据类型;但是需要注意的时,使用这种方式时,要求后端不能设置ContentNegotiationConfigurer.ignoreAcceptHeader(true)

在实际使用这种方式的时候,客户端需要额外注意,Accept 请求头中定义的 MediaType 的顺序,是优于后端定义的 produces 顺序的,因此用户需要将自己实际希望接受的数据类型放在前面,或者干脆就只设置一个

方式三

借助ContentNegotiationConfigurer实现通过请求参数来决定返回类型,常见的配置方式形如

configurer.favorParameter(true)
        // 禁用accept协商方式,即不关心前端传的accept值
      //                .ignoreAcceptHeader(true)
        // 哪个放在前面,哪个的优先级就高; 当上面这个accept未禁用时,若请求传的accept不能覆盖下面两种,则会出现406错误
        .defaultContentType(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)
        // 根据传参mediaType来决定返回样式
        .parameterName("mediaType")
        // 当acceptHeader未禁用时,accept的值与mediaType传参的值不一致时,以mediaType的传值为准
        // mediaType值可以不传,为空也行,但是不能是json/xml之外的其他值
        .mediaType("json", MediaType.APPLICATION_JSON)
        .mediaType("xml", MediaType.APPLICATION_XML);

即添加这个设置之后,最终的表现为:

  1. 请求参数指定的返回类型,优先级最高,返回指定参数对应的类型
  2. 没有指定参数时,选择 defaultContentType 定义的默认返回类型与接口 produce中支持的求交集,优先级则按照 defaultContentType 中定义的顺序来选择
  3. 没有指定参数时,若此时还有 accept 请求头,则请求头中定义顺序的优先级高于 defaultContentType, 高于 produce

注意注意:当配置中忽略了 AcceptHeader 时,.ignoreAcceptHeader(true),上面第三条作废

最后的最后,本文所有的源码可以再下面的 git 中获取;本文的知识点已经汇总在《一灰灰的 Spring 专栏》 两百多篇的原创系列博文,你值得拥有;我是一灰灰,咱们下次再见

III. 不能错过的源码和相关知识点

0. 项目

  • 工程:https://github.com/liuyueyi/spring-boot-demo
  • 源码:https://github.com/liuyueyi/spring-boot-demo/tree/master/spring-boot/204-web-xml-json

1. 微信公众号: 一灰灰 Blog

尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现 bug 或者有更好的建议,欢迎批评指正,不吝感激

下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

  • 一灰灰 Blog 个人博客 https://blog.hhui.top
  • 一灰灰 Blog-Spring 专题博客 http://spring.hhui.top

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

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

相关文章

Linux内核学习笔记——内核页表隔离KPTI机制

接前文。 一步一步理解CPU芯片漏洞&#xff1a;Meltdown与Spectre ARM系列之MMU TLB和ASID基础概念介绍。 一、Meltdown & Spectre 漏洞 Meltdown 和 Spectre 这两个漏洞厉害的地方就在于&#xff0c;利用现代CPU speculative execution (预测执行)的漏洞&#xff0c;在…

退役记——破铜烂铁的一生

写在前面 今天刚刚结束大三上的所有课程。我慢慢鼓起勇气去整理这段零碎的竞赛记忆&#xff0c;或许是最终也没拿到一个满意的奖项&#xff0c;来给我的竞赛生涯画上一个圆满的句号。 我该怎么回忆这破铜烂铁的一生&#xff0c;上万次尝试提交、数以千计的习题、上百次练习赛…

01、Java 数据结构:数据结构和算法的概述

数据结构和算法的概述1 参考教材2 数据结构2.1 数据的逻辑结构2.2 数据的存储结构2.3 数据的运算3 基本的数学概念的复习3.1 函数的定义3.2 极限3.3 对数4 算法4.1 算法的基本特性4.2 算法设计的要求4.3 时间复杂度和空间复杂度是衡量算法优劣的重要指标1 参考教材 主要参考的…

wy的leetcode刷题记录_Day62——二叉树结束

wy的leetcode刷题记录_Day62 声明 本文章的所有题目信息都来源于leetcode 如有侵权请联系我删掉! 时间&#xff1a;2022-12-27 前言 目录wy的leetcode刷题记录_Day62声明前言1750. 删除字符串两端相同字符后的最短长度题目介绍思路代码收获108. 将有序数组转换为二叉搜索树题…

各类遥测终端机RTU/水文遥测终端机简介

平升电子测遥测终端机RTU/水文遥测终端机基于4G、5G、NB-IoT、光纤、北斗三号卫星等通信网络&#xff0c;实现数据采集、存储、处理分析、传输&#xff0c;远程/自动控制现场泵、闸、阀等设备运行。它广泛应用于智慧水利领域的灌区信息化、水库安全监测、山洪灾害预警、水资源税…

泛型学习(java)

1.泛型的理解和好处 1.1看一个需求 1)请编写程序&#xff0c;在ArrayList中&#xff0c;添加3个Dog对象 Dog对象含有name和age,并输出name和age(要求使用getXxx()) 先使用传统的方法来解决->引出泛型 import java.util.ArrayList;public class Generic01 {public stati…

前端性能优化(一):指标和工具

目录 一&#xff1a;性能指标和优化目标 1.1.网络加载性能 1.2.用户交互体验 二&#xff1a;RAIL测量模型 2.1.Response&#xff08;响应&#xff09;: 处理事件应在在50ms内完成 2.2.Animation&#xff08;动画&#xff09;: 每10ms产生一帧 2.3.Idle&#xff08;空闲&…

SpringBoot — 初始创建项目小白教程

这里写目录标题前言SpringBoot简介重要策略Spring Boot 项目约定IntelliJ IDEA 直接创建Maven项目改造创建常见项目结构代码层资源文件结构主要文件说明SpringBootApplication 注解分析总结前言 使用 Servlet/JSP 开发 JavaWeb 时&#xff0c;一个接口对应一个Servlet&#xf…

mysql分区之RANGE类型

目录 首先查看MySQL是否支持分区 在实际操作分区前我们得了解下分区的几点限制&#xff1a; RANGE分区实操 SQL如何查询分区数据 首先查看MySQL是否支持分区 show plugins; 当查询结果显示partition的状态为active则表示当前MySQL版本支持分区。分区方案一般有四种&#…

CVPR 2017|SfMLearner:单目视频中深度和姿态估计的无监督算法

&#x1f3c6;作者提出了一个单目相机的视频序列进行深度估计与运动估计&#xff0c;作者的方法是完全无监督的&#xff0c;端到端的学习&#xff0c;作者使用了单视角深度网络和多姿态网络&#xff0c;提出了一个图像&#xff08;predict&#xff09;与真实的下一帧&#xff0…

09---Vue使用路由

由于之前数据、主页全部放在Home.vue中&#xff0c;不能够实现复用&#xff0c;于是&#xff0c;现在进行拆分&#xff0c;拆分出数据主体&#xff08;user.vue&#xff09;&#xff0c;侧边栏&#xff08;aside&#xff09;&#xff0c;顶部栏&#xff08;Header&#xff09;&…

SpringCloudAlibaba 学习笔记

❤ 作者主页&#xff1a;Java技术一点通的博客 ❀ 个人介绍&#xff1a;大家好&#xff0c;我是Java技术一点通&#xff01;(&#xffe3;▽&#xffe3;)~* &#x1f34a; 记得关注、点赞、收藏、评论⭐️⭐️⭐️ &#x1f4e3; 认真学习&#xff0c;共同进步&#xff01;&am…

疫情卷土重来,如何利用SRM系统打造数字化的“免疫系统”

2022年末&#xff0c;国内疫情再次卷土重来&#xff0c;形势严峻。国内企业也再次面临业务发展的压力。但实际上&#xff0c;在疫情常态化后&#xff0c;我国许多大中型企业都建立了全面的数字化“免疫系统”&#xff0c;增强了抗风险和跨周期的能力&#xff0c;大大增强了抵御…

【B站】Presto + Alluxio:B站数据库系统性能提升实践

欢迎来到【微直播间】&#xff0c;2min纵览大咖观点 在日常线上生产环境中有大量的数据需要被访问&#xff0c;为了保证数据同步以及查询效率&#xff0c;需要耗费较大的资源&#xff0c;同时&#xff0c;很多被查询和访问的数据是重复的&#xff0c;这对数据库系统造成极大压…

C++类和对象概念及实现详解(下篇)

文章目录 一、类的六个默认成员函数详解 1、拷贝构造 1、1 拷贝构造的引入及概念 1、2 拷贝构造函数的特征 2、赋值运算符重载 2、1 运算符重载 2、2 赋值运算符重载 3、普通对象取地址 4、const对象取地址 二、类和对象重点知识点 1、初始化列表 2、static成员 3、友元函数 4、…

一文带你走进MySQL索引

文章目录索引1. 索引的介绍2. 索引的本质3. 索引的结构3.1 Hash3.2 B树3.3 常见面试题之为什么用B树4. 索引的分类4.1 功能逻辑层次4.2 存储形式层次5. 索引的失效5.1 最左前缀原则5.2 索引失效的场景6. 索引常见面试题7. 总结及参考文献索引 1. 索引的介绍 索引是通过某种算…

潘多拉-视频播放器,一个轻量的视频播放器

潘多拉-视频播放器 轻量视频播放器,该项目是从https://github.com/getActivity/AndroidProject-Kotlin 中抽离出的一个视频播放器,之前没有 单独设置项目,我在使用过程中觉得这个挺方便好用的,所以为了方便使用,单独剥离出来,可以单独在项目中使用,后续我也会基于这个项目进行…

【数据结构】Leetcode旋转数组

目录 一、题目说明 二、题目解析 一、题目说明 题目链接&#xff1a;leetcode旋转数组 给你一个数组&#xff0c;将数组中的元素向右轮转k个位置&#xff0c;其中k是非负数。 示例1&#xff1a; 输入&#xff1a;nums [1,2,3,4,5,6,7],k 3 输出&#xff1a;[5,6,7,1,2,3,4…

给大家分享5款轻便小巧的好软件

随着网络信息技术的发展&#xff0c;越来越多的人在办公时需要用到电脑了。如果你想提高办公效率&#xff0c;那么就少不了工具的帮忙&#xff0c;今天给大家分享5款办公必备的好软件。 1.开源分享工具——ShareX ShareX 是一款 Windows 上开源的截图、文件共享和生产力工具&…

IB 生申请德国大学的条件

Hallo! 你是IB生吗&#xff1f;是否有去德国读大学的意愿&#xff1f;来看看IB生申请德国大学的条件吧~ 01IB课程简介 IB课程包括六个学科组&#xff0c;其中一个必须从 IBO 指定的第 1 到第 5 个科目组中选修&#xff0c;另外还有一个可选科目&#xff0c;可以从六个科目组中选…