Swagger:同一状态码返回不同的 Response 对象
1、概览
本文介绍了如何在 API 规范中,为同一个响应定义多个不同的对象,以及如何使用该规范生成 Java 代码和 Swagger 文档。
2、问题陈述
定义两个对象(object)。
Car
对象的属性是owner
和plate
,两者都是String
。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':
# 返回 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-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>
注意,我们设置了 interfaceOnly=true
,也就是说只生成接口。不生成其他我们没兴趣的文件。
执行插件:
mvn clean compile
你可以看到生成的文件如下:
- 生成了
Car
和Bike
对象。 - 由于使用了
@JsonSubTypes
注解,因此生成了OneOfinlineResponse200
接口来表示可以是Car
或Bike
的对象 InlineResponse200
是OneOfinlineResponse200
的基本实现。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
访问。
可以看到有一个下拉菜单,可以在 Car
和 Bike
示例之间切换:
Response Schema
也已正确呈现:
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 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 小节中的方法生成文档。
如你所见,描述正确地显示了:
CarOrBike
Model 的描述与预期相符:
参考:https://www.baeldung.com/swagger-two-responses-one-response-code