SpringBoot集成OpenAPI

引入依赖

1
2
3
4
5
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.6.0</version>
</dependency>

API文档

默认 OpenAPI 描述路径

http://localhost:8080/v3/api-docs

自定义路径

springdoc.api-docs.path=/api-docs

此时, 访问路径变为

http://localhost:8080/api-docs

默认是json格式的, 也可以配置为yaml格式

http://localhost:8080/api-docs.yaml

Swagger UI

默认访问路径

http://localhost:8080/swagger-ui/index.html

自定义路径配置

springdoc.swagger-ui.path=/swagger-ui-custom.html

此时访问 http://localhost:8080/swagger-ui-custom.html 也会重定向到

http://localhost:8080/swagger-ui/index.html

OpenAPI数据格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
{
"openapi": "3.0.1",
"info": {
"title": "OpenAPI definition",
"version": "v0"
},
"servers": [
{
"url": "http://localhost:8080",
"description": "Generated server url"
}
],
"paths": {
"/api/v2/post/{id}": {
"post": {
"tags": [
"x-2-controller"
],
"operationId": "post",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "name",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/User"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "OK",
"content": {
"*/*": {
"schema": {
"type": "object"
}
}
}
}
}
}
},
"/api/v1/post/{id}": {
"post": {
"tags": [
"x-controller"
],
"operationId": "post_1",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "name",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/User"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "OK",
"content": {
"*/*": {
"schema": {
"type": "object"
}
}
}
}
}
}
},
"/api/v2/get/{id}": {
"get": {
"tags": [
"x-2-controller"
],
"operationId": "get",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "name",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"*/*": {
"schema": {
"type": "object"
}
}
}
}
}
}
},
"/api/v1/get/{id}": {
"get": {
"tags": [
"x-controller"
],
"operationId": "get_1",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "name",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"*/*": {
"schema": {
"type": "object"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"Role": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
},
"User": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "integer",
"format": "int32"
},
"roles": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Role"
}
}
}
}
}
}
}

上述数据结构用到的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RestController
@RequestMapping("/api/v1")
public class XController {

@GetMapping("/get/{id}")
public Object get(@PathVariable String id, @RequestParam String name) {
return name;
}

@PostMapping("/post/{id}")
public Object post(@PathVariable String id, @RequestParam String name, @RequestBody User user) {
return user;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RestController
@RequestMapping("/api/v2")
public class X2Controller {

@GetMapping("/get/{id}")
public Object get(@PathVariable String id, @RequestParam String name) {
return name;
}

@PostMapping("/post/{id}")
public Object post(@PathVariable String id, @RequestParam String name, @RequestBody User user) {
return user;
}
}
1
2
3
4
5
6
7
8
9
10
@Data
public class User implements Serializable {

private String name;

private Integer age;

private List<Role> roles;

}
1
2
3
4
5
6
@Data
public class Role implements Serializable {

private String name;

}

合并离线文档

引入新的maven依赖

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-common</artifactId>
<version>1.6.15</version>
</dependency>
<dependency>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser</artifactId>
<version>2.0.30</version>
</dependency>

合并实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.example.httpdemo;

import io.swagger.parser.OpenAPIParser;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.parser.core.models.ParseOptions;
import lombok.SneakyThrows;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.FileCopyUtils;

import java.nio.charset.StandardCharsets;

@Configuration
public class OpenApiConfig {

@SuppressWarnings("all")
@Bean
public OpenAPI customOpenAPI() {
// 读取本地JSON文件
String mainJson = loadApiJson("local-api.json");
String additionalJson = loadApiJson("additional-api-doc.json");

// 解析JSON为OpenAPI对象
OpenAPIParser parser = new OpenAPIParser();
ParseOptions options = new ParseOptions();
options.setResolve(true);

// 主文档
OpenAPI mainOpenAPI = parser.readContents(mainJson, null, options).getOpenAPI();

// 扩展文档
OpenAPI additionalOpenAPI = parser.readContents(additionalJson, null, options).getOpenAPI();

// 合并
mainOpenAPI.getPaths().putAll(additionalOpenAPI.getPaths());
mainOpenAPI.getComponents().getSchemas().putAll(additionalOpenAPI.getComponents().getSchemas());

return mainOpenAPI;
}

@SneakyThrows
private String loadApiJson(String path) {
ClassPathResource resource = new ClassPathResource(path);
byte[] bdata = FileCopyUtils.copyToByteArray(resource.getInputStream());
return new String(bdata, StandardCharsets.UTF_8);
}

}

json为OpenAPI结构完整json

如果想了解model结构, 可以将上述json反序列化为 OpenAPI 对象, 实体定义在

1
2
3
4
5
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-models</artifactId>
<version>2.2.8</version>
</dependency>

OpenAPI规范

https://openapi.apifox.cn