Swagger使用相同的响应代码指定两个响应

2023/05/12

1. 概述

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

2. 问题的提出

让我们定义两个对象,Car有一个owner和一个plate作为属性,两者都是字符串类型。另一方面,Bike类有owner和speed两个字段,speed是一个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':
                # return Car or 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: tuyucheng
            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: 1.0.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: tuyucheng
                                        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-codegen和openapi-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>

请注意,我们决定切换选项以仅生成接口,以便避免生成大量对我们来说不是很感兴趣的文件。

现在让我们执行插件:

mvn clean compile

我们现在可以查看生成的文件:

  • 生成Car和Bike对象
  • 由于使用了@JsonSubTypes注解,生成了OneOfinlineResponse200接口来表示可以是Car或Bike的对象
  • InlineResponse200是OneOfinlineResponse200的基础实现
  • VehicleApi定义端点:对此端点的获取请求返回InlineResponse200

3.3 生成Swagger UI文档

要从我们的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>

springdoc-openapi-ui的1.6.10版本依赖swagger-ui版本4.13.2,它可以正确处理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以指定我们不想从控制器生成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访问。

我们可以看到有一个下拉菜单可以在Car和Bike示例之间导航:

响应模式也被正确呈现:

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

在OpenAPI 2中,oneOf不存在。因此,让我们找到一个替代方案。

4.1 构建描述符文件

我们能做的最好的事情就是定义一个包装对象,它将具有Car和Bike的所有属性。公共属性将是必需的,并且仅属于其中一个的属性将保持可选:

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响应将是一个CarOrBike对象,我们将在描述中添加更多见解。不幸的是,我们无法添加各种示例,因此我们决定只给出一个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: 1.0.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: tuyucheng
                            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对象包含预期的字段,具有@NotNull所有者
  • VehicleApi定义端点,对此端点的获取请求返回CarOrBike

4.3 生成Swagger UI文档

我们可以像在3.3小节中那样生成文档

然后可以看到我们的描述显示:

我们的CarOrBike模型也如预期的那样被描述:

5. 总结

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

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章