概述
在这篇文章中,我们将编写一个API规范,允许对同一个响应代码返回两种不同的对象。我们将演示如何使用这个规范来生成Java代码和Swagger文档。
问题描述
首先,我们定义两个对象:一辆汽车(Car)有车主和车牌,两者都是字符串(String)。另一方面,一辆自行车(Bike)有车主和速度,速度是一个整数(Integer)。
在OpenAPI中,这些定义对应于以下描述:
Car:
type: object
properties:
owner:
type: string
plate:
type: string
Bike:
type: object
properties:
owner:
type: string
speed:
type: integer
我们的目标是描述一个名为/vehicle
的端点,它将接受GET请求,并能够返回汽车或自行车。换句话说,我们要完成以下描述:
paths:
/vehicle:
get:
responses:
'200':
# return Car or Bike
我们将讨论OpenAPI 2和3规范下的这个话题。
使用OpenAPI 3实现两种不同的响应
OpenAPI 3版本引入了oneOf
,这正是我们需要的。
3.1. 构建描述文件
在OpenAPI 3规范中,oneOf
期望一个对象数组,表示提供的值应精确匹配给定对象之一:
schema:
oneOf:
- $ref: '#/components/schemas/Car'
- $ref: '#/components/schemas/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
最后,让我们看看完整的描述文件:
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-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.52</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
我们可以看到生成的文件:
- 汽车和自行车对象被生成
- 使用
@JsonSubTypes
注解,OneOfinlineResponse200
接口被生成,用于表示可以是汽车或自行车的对象 -
InlineResponse200
是OneOfinlineResponse200
的基础实现 -
VehicleApi
定义了端点:向此端点发送GET请求将返回一个InlineResponse200
3.3. 生成Swagger UI文档
要从我们的YAML描述文件生成Swagger UI文档,我们将使用springdoc-openapi。在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
依赖于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
通过http://localhost:8080/swagger-ui/index.html
可以访问Swagger UI。
我们可以看到有一个下拉菜单,可以在汽车和自行车示例之间切换:
响应的Schema
也正确显示:
使用OpenAPI 2实现两种不同的响应
在OpenAPI 2中,没有oneOf
。让我们找一个替代方案。
4.1. 构建描述文件
最好的做法是定义一个包含汽车和自行车所有属性的包装对象。 公共属性将是必需的,而只属于其中一个的属性将保持可选:
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
对象。我们会增加更多描述。遗憾的是,我们不能添加多个示例,所以我们决定只给出一个汽车的例子。
让我们看看最终的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
对象包含预期字段,车主字段带有@NotNull
注解 -
VehicleApi
定义了端点:向此端点发送GET请求将返回一个CarOrBike
对象
4.3. 生成Swagger UI文档
我们可以通过与3.3节相同的方式生成文档。
我们可以看到描述内容:
以及我们预期的CarOrBike
模型描述:
结论
在这个教程中,我们了解了如何为一个可以返回一个对象或另一个对象的端点编写OpenAPI规范。我们利用YAML描述文件通过swagger-codegen
生成Java代码,并使用springdoc-openapi-ui
生成Swagger UI文档。
如往常一样,代码可在GitHub上找到。