乐趣区

关于前端:SpringBoot系列教程之定义接口返回类型的几种方式

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

实现一个 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");
    }
}

下面的实现中

produces = application/xml
produces = applicatin/json

接下来咱们拜访一下看看返回的是否和预期统一

从下面截图也能够看出,xml 接口返回的是 xml 格局数据;json 接口返回的是 json 格局数据

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

下面的形式,十分直观,天然咱们就会有一个疑难,当接口上不指定 produces 属性时,间接拜访会怎么体现呢?

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

请留神下面的截图,两种拜访形式返回的数据类型不统一

application/xhtml+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);
复制代码 </pre>

下面这三行代码,次要就是说,当初能够依据传参 mediaType 来管制返回的类型,咱们新增一个接口来验证一下

<pre class="prettyprint hljs dart">@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);

即增加这个设置之后,最终的体现为:

produce

留神留神:当配置中疏忽了 AcceptHeader 时,.ignoreAcceptHeader(true),下面第三条作废

退出移动版