Swagger: Specify Two Responses with the Same Response Code – Swagger 用相同的响应代码指定两个响应

最后修改: 2022年 8月 24日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this article, we’ll write an API specification that allows returning two different objects for the same response code. We’ll demonstrate how we can use that specification to generate Java code and the Swagger documentation.

在这篇文章中,我们将编写一个API规范,允许为同一个响应代码返回两个不同的对象。我们将演示如何使用该规范来生成Java代码和Swagger文档。

2. Presentation of the Problem

2.问题的提出

Let’s define two objects. A Car has an owner and a plate as attributes, both being Strings. On the other hand, a Bike has an owner and speed. The speed is an Integer.

让我们来定义两个对象。一个汽车有一个车主和一个车牌作为属性,两者都是字符串。另一方面,Bike有一个所有者和速度。速度是一个整数

Using OpenAPI, these definitions correspond to the following description:

使用OpenAPI,这些定义对应于以下描述。

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

We want to describe an endpoint /vehicle which will accept GET requests and will be able to return either a Car or a Bike. That is to say, we want to complete the following descriptor:

我们想描述一个端点/vehicle,它将接受GET请求,并能够返回CarBike也就是说,我们想完成以下描述。

paths:
  /vehicle:
    get:
      responses:
        '200':
          # return Car or Bike

We’ll discuss this topic for both OpenAPI 2 and 3 specifications.

我们将为OpenAPI 2和3规范讨论这个话题。

3. Having Two Different Responses With OpenAPI 3

3.使用OpenAPI 3有两种不同的响应

OpenAPI version 3 introduced oneOf, which is exactly what we need.

OpenAPI第3版引入了oneOf,这正是我们需要的东西。

3.1. Building the Descriptor File

3.1.建立描述符文件

In OpenAPI 3 specification, oneOf expects an array of objects and indicates that the provided value should exactly match one of the given objects:

在OpenAPI 3规范中,oneOf期望一个对象数组,并表示所提供的值应与给定对象之一完全匹配:

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

Besides, OpenAPI 3 introduced the possibility to showcase various examples of responses. For clarity, we definitely want to provide at least an example response with a Car and another one with a Bike:

此外,OpenAPI 3引入了展示各种响应实例的可能性。为了清晰起见,我们肯定希望至少提供一个带有汽车的响应示例和另一个带有自行车的响应示例。

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

Finally, let’s have a look at our whole descriptor file:

最后,让我们看一下我们的整个描述符文件。

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. Generate Java Classes

3.2.生成Java类

Now, we’ll use our YAML file to generate our API interfaces. Two maven plugins, swagger-codegen, and openapi-generator, can be used to generate Java code from the api.yaml file. As of version 6.0.1, openapi-generator doesn’t handle oneOf, so we’ll stick to swagger-codegen in this article.

现在,我们将使用YAML文件生成API接口。两个maven插件,swagger-codegen,以及openapi-generator可以用来从api.yaml文件生成Java代码。从6.0.1版本开始,openapi-generator不能处理oneOf,所以在本文中我们将坚持使用swagger-codegen

We’ll use the following configuration for the swagger-codegen plugin:

我们将对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>

Let’s note that we decided to toggle the option to generate only the interfaces in order to spare the generation of a lot of files that are not very interesting to us.

让我们注意到,我们决定切换只生成界面的选项,以便免于生成大量对我们来说不怎么重要的文件。

Let’s now execute the plugin:

现在我们来执行这个插件。

mvn clean compile

We can now have a look at the generated files:

我们现在可以看一下生成的文件了。

  • the Car and Bike objects are generated
  • the OneOfinlineResponse200 interface is generated to represent the object that can be either a Car or a Bike, thanks to the use of the @JsonSubTypes annotation
  • InlineResponse200 is the base implementation of OneOfinlineResponse200
  • VehicleApi defines the endpoint: a get request to this endpoint returns an InlineResponse200

3.3. Generate the Swagger UI Documentation

3.3.生成Swagger用户界面文档

To generate the Swagger UI documentation from our YAML descriptor file, we’ll use springdoc-openapi. Let’s add the dependency to springdoc-openapi-ui to our pom.xml:

为了从我们的YAML描述符文件中生成Swagger UI文档,我们将使用springdoc-openapi。让我们把对springdoc-openapi-ui的依赖性添加到我们的pom.xml。

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

Version 1.6.10 of springdoc-openapi-ui depends on swagger-ui version 4.13.2, which handles correctly oneOf and various response examples.

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

To generate the Swagger UI documentation from the YAML file, we need to declare a SpringBootApplication and add the three following beans:

为了从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);
}

Last but not least, we’ll need to make sure our YAML descriptor is inside the resources/static directory and update the application.properties to specify that we don’t want to generate the Swagger UI from the Controllers but from the YAML file:

最后但同样重要的是,我们需要确保我们的YAML描述符在resources/static目录内,并更新application.properties以指定我们不希望从Controllers生成Swagger UI,而是从YAML文件生成。

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

We can now start our application:

我们现在可以启动我们的应用程序。

mvn spring-boot:run

The Swagger UI is accessible through http://localhost:8080/swagger-ui/index.html.

Swagger用户界面可以通过http://localhost:8080/swagger-ui/index.html访问。

We can see that there is a dropdown to navigate between the Car and Bike examples:

我们可以看到,有一个下拉菜单可以在汽车自行车例子之间进行导航。

 

swaggerResponse1

swaggerResponse1

The response Schema is also correctly rendered:

响应Schema也被正确呈现。

 

swaggerResponse2

swaggerResponse2

4. Having Two Different Responses With OpenAPI 2

4.使用OpenAPI 2有两种不同的响应

In OpenAPI 2, oneOf didn’t exist. So let’s find an alternative.

在OpenAPI 2中,oneOf并不存在。所以我们要找到一个替代方案。

4.1. Build the Descriptor File

4.1.建立描述符文件

The best we can do is to define a wrapping object which will have all properties of Car and Bike. The common properties will be required, and the properties that belong to only one of them will remain optional:

我们能做的最好的事情是定义一个包装对象,它将拥有汽车自行车的所有属性。共同的属性将是必需的,而只属于其中之一的属性将保持可选性。

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

Our API response will be a CarOrBike object. We’ll add more insight in the description. Unfortunately, we can’t add various examples, so we decide to give only an example of a Car.

我们的API响应将是一个CarOrBike对象。我们将在描述中加入更多的见解。不幸的是,我们不能添加各种例子,所以我们决定只给出一个汽车的例子。

Let’s have a look at the resulting api.yaml:

让我们来看看产生的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. Generate Java Classes

4.2.生成Java类

Let’s adapt our swagger-codegen plugin configuration to parse the OpenAPI 2 file. For this, we need to use version 2.x of the plugin. It was also located in another package:

让我们调整我们的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>

Let’s now have a look at the generated files:

现在我们来看看生成的文件。

  • the CarOrBike object contains the expected fields, with an @NotNull owner
  • VehicleApi defines the endpoint: a get request to this endpoint returns a CarOrBike

4.3. Generate the Swagger UI Documentation

4.3.生成Swagger用户界面文档

We can generate the documentation in the same way as we did in 3.3.

我们可以按照3.3.中的相同方式生成文档。

We can see that our description is shown:

我们可以看到,我们的描述被显示出来。

 

swaggerResponse3

swaggerResponse3

And our CarOrBike model is described as expected:

而我们的汽车或自行车模型被描述为预期。

 

SwaggerResponse4

SwaggerResponse4.

5. Conclusion

5.总结

In this tutorial, we understood how to write an OpenAPI specification for an endpoint that can return one object or another. We used the YAML descriptor to generate Java code thanks to swagger-codegen and to generate the Swagger UI documentation with springdoc-openapi-ui.

在本教程中,我们了解了如何为一个可以返回一个对象的端点写一个OpenAPI规范。由于swagger-codegen,我们使用YAML描述符来生成Java代码,并使用springdoc-openapi-ui来生成Swagger UI文档。

As usual, the code is available over on GitHub.

像往常一样,代码可在GitHub上获得。