Swagger:同一状态码返回不同的 Response 对象

1、概览

本文介绍了如何在 API 规范中,为同一个响应定义多个不同的对象,以及如何使用该规范生成 Java 代码和 Swagger 文档。

2、问题陈述

定义两个对象(object)。

  • Car 对象的属性是 ownerplate,两者都是 String
  • Bike 对象的属性是 ownerspeedspeed 是一个 Integer

它们在 OpenAPI 中的描述如下:

Car:
  type: object
  properties:
    owner:
      type: string
    plate:
      type: string
Bike:
  type: object
  properties:
    owner:
      type: string
    speed:
      type: integer

我们想描述一个 /vehicle 端点,它将接受 GET 请求,并能返回一个 Car 或一个 Bike

类似于如下描述:

paths:
  /vehicle:
    get:
      responses:
        '200':
          # 返回 Car 或 Bike

接下来介绍在 OpenAPI 2 和 3 规范中的不同实现方式。

3、OpenAPI 3 实现不同响应对象

OpenAPI 3 引入了 oneOf,可以实现这个需求。

3.1、创建描述文件

在 OpenAPI 3 规范中,oneOf 关键字需要一个数组,表示响应的对象,是数组中的之一。

schema:
  oneOf:
    - $ref: '#/components/schemas/Car'
    - $ref: '#/components/schemas/Bike'

OpenAPI 3 还可以为不同的对象定义示例。如下,提供了一个 Car 响应示例和一个 Bike 响应示例:

examples:
  car:
    summary: an example of car
    value:
      owner: baeldung
      plate: AEX305
  bike:
    summary: an example of bike
    value:
      owner: john doe
      speed: 25

最后,让我们来看看完整的描述符文件:

openapi: 3.0.0
info:
  title: Demo api
  description: Demo api for the article 'specify two responses with same code based on optional parameter'
  version: 0.1.0
paths:
  /vehicle:
    get:
      responses:
        '200':
          description: Get a vehicle 
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/Car'
                  - $ref: '#/components/schemas/Bike'
              examples:
                car:
                  summary: an example of car
                  value:
                    owner: baeldung
                    plate: AEX305
                bike:
                  summary: an example of bike
                  value:
                    owner: john doe
                    speed: 25
components:
  schemas:
    Car:
      type: object
      properties:
        owner:
          type: string
        plate:
          type: string
    Bike:
      type: object
      properties:
        owner:
          type: string
        speed:
          type: integer

3.2、生成 Java 类

现在,使用 YAML 文件来生成 API 接口。有两个 maven 插件:swagger-codegenopenapi-generator 可用于从 api.yaml 文件生成 Java 代码。从 6.0.1 版开始,openapi-generator 无法处理 oneOf,因此本文将继续使用 swagger-codegen

配置 swagger-codegen 插件,如下:

<plugin>
    <groupId>io.swagger.codegen.v3</groupId>
    <artifactId>swagger-codegen-maven-plugin</artifactId>
    <version>3.0.34</version>
    <executions>
        <execution>
            <goals>
                <goal>generate</goal>
            </goals>
            <configuration>
                <inputSpec>${project.basedir}/src/main/resources/static/api.yaml</inputSpec>
                <language>spring</language>
                <configOptions>
                    <java8>true</java8>
                    <interfaceOnly>true</interfaceOnly>
                </configOptions>
            </configuration>
        </execution>
    </executions>
</plugin>

注意,我们设置了 interfaceOnly=true,也就是说只生成接口。不生成其他我们没兴趣的文件。

执行插件:

mvn clean compile

你可以看到生成的文件如下:

  • 生成了 CarBike 对象。
  • 由于使用了 @JsonSubTypes 注解,因此生成了 OneOfinlineResponse200 接口来表示可以是 CarBike 的对象
  • InlineResponse200OneOfinlineResponse200 的基本实现。
  • VehicleApi 定义了端点:对该端点的获取请求会返回一个 InlineResponse200

3.3、生成 Swagger UI 文档

使用 springdoc-openapi 根据 YAML 描述文件生成 Swagger UI 文档。

pom.xml 中添加 springdoc-openapi-ui 依赖:

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-ui</artifactId>
    <version>1.6.10</version>
</dependency>

1.6.10 版的 springdoc-openapi-ui 依赖了 4.13.2 版的 swagger-ui,因此它能正确处理 oneOf 和各种响应示例。

要从 YAML 文件生成 Swagger UI 文档,需要声明一个 SpringBootApplication 并添加以下三个 Bean,如下:

@Bean
SpringDocConfiguration springDocConfiguration() {
    return new SpringDocConfiguration();
}

@Bean
SpringDocConfigProperties springDocConfigProperties() {
    return new SpringDocConfigProperties();
}

@Bean
ObjectMapperProvider objectMapperProvider(SpringDocConfigProperties springDocConfigProperties) {
    return new ObjectMapperProvider(springDocConfigProperties);
}

注意,需要确保 YAML 描述符位于 resources/static 目录中。并更新 application.properties,指定我们不想从 Controller 生成 Swagger UI,而是从 YAML 文件生成:

springdoc.api-docs.enabled=false
springdoc.swagger-ui.url=/api.yaml

现在,启动应用程序:

mvn spring-boot:run

Swagger UI 可通过 http://localhost:8080/swagger-ui/index.html 访问。

可以看到有一个下拉菜单,可以在 CarBike 示例之间切换:

Swagger Response Model

Response Schema 也已正确呈现:

Swagger Response Schema

4、OpenAPI 2 实现不同响应对象

OpenAPI 2 没有 oneOf,因此,需要另辟蹊径。

4.1、创建描述符文件

最好的办法就是定义一个封装对象,具有 CarBike 的所有属性。共同的属性是必需的,其他属性则是可选的。

CarOrBike:
  description: a car will have an owner and a plate, whereas a bike has an owner and a speed
  type: object
  required:
    - owner
  properties:
    owner:
      type: string
    plate:
      type: string
    speed:
      type: integer

API Response 是一个 CarOrBike 对象,在 description 中有进一步说明。

这里给出了一个 Car 的示例。

生成的 api.yaml 如下:

swagger: 2.0.0
info:
  title: Demo api
  description: Demo api for the article 'specify two responses with same code based on optional parameter'
  version: 0.1.0
paths:
  /vehicle:
    get:
      responses:
        '200':
          description: Get a vehicle. Can contain either a Car or a Bike
          schema:
            $ref: '#/definitions/CarOrBike'
          examples:
            application/json:
              owner: baeldung
              plate: AEX305
              speed:
definitions:
  Car:
    type: object
    properties:
      owner:
        type: string
      plate:
        type: string
  Bike:
    type: object
    properties:
      owner:
        type: string
      speed:
        type: integer
  CarOrBike:
    description: a car will have an owner and a plate, whereas a bike has an owner and a speed
    type: object
    required:
      - owner
    properties:
      owner:
        type: string
      plate:
        type: string
      speed:
        type: integer

4.2、生成 Java 类

调整 swagger-codegen 插件配置,以解析 OpenAPI 2 文件。

需要使用位于另一个包的 2.x 版本的插件:

<plugin>
    <groupId>io.swagger</groupId>
    <artifactId>swagger-codegen-maven-plugin</artifactId>
    <version>2.4.27</version>
    <executions>
        <execution>
            <goals>
                <goal>generate</goal>
            </goals>
            <configuration>
                <inputSpec>${project.basedir}/src/main/resources/static/api.yaml</inputSpec>
                <language>spring</language>
                <configOptions>
                    <java8>true</java8>
                    <interfaceOnly>true</interfaceOnly>
                </configOptions>
            </configuration>
        </execution>
    </executions>
</plugin>

生成的文件如下:

  • CarOrBike 对象包含预期的字段,owner 注解了 @NotNull
  • VehicleApi 定义了端点:向该端点发出的获取请求会返回一个 CarOrBike

4.3、生成 Swagger UI 文档

我们可以按照 3.3 小节中的方法生成文档。

如你所见,描述正确地显示了:

swagger response

CarOrBike Model 的描述与预期相符:

Swagger Response


参考:https://www.baeldung.com/swagger-two-responses-one-response-code