Table of contents
1 OpenAPI 簡介
OpenAPI Specification(OAS)係由 Swagger Specification 演變出黎既一套 HTTP API 格式規範,係開發者之間表達 HTTP APIs 既其中一個標準。
喺 2015,SmartBear 公司收購左開源既 Swagger Specification,然後喺 2015 年年尾,SmartBear 將 Swagger Specification 捐左畀 Linux Foundation 旗下既 OpenAPI Initiative 公司,亦將佢改名成 OpenAPI Specification,亦即係 Swagger 2.0
➜ OpenAPI 3.0.0
。
至於 Swagger UI 就係一個工具,準確黎講係一個網頁既介面,畀我地好容易咁測試到我地寫既 HTTP endpoints。
1.1 API-first 開發方式
API-first 就係以 API 主導既開發方式,我地會先寫 API contract,亦即係 Swagger/OpenAPI Specification,然後再寫 code。
以下係熱門既 cloud platforms 既 API import 功能支援既 OpenAPI Specification 版本:
Cloud platforms | 支援既 OpenAPI Specification 版本 |
---|
Amazon AWS API Gateway | 2.0 、3.0 |
Microsoft Azure API Management | 2.0 、3.0 、3.1 |
Google Cloud API Gateway | 2.0 |
Alibaba Cloud API Gateway | 2.0 、3.0 |
參考資料:
2 動手寫
我地會寫一個有齊曬模擬既 CRUD endpoints 既 Spring Boot MVC 項目。
2.1 Maven dependencies
1<?xml version="1.0" encoding="UTF-8"?>
2<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3 <modelVersion>4.0.0</modelVersion>
4
5 <groupId>com.michael</groupId>
6 <artifactId>Spring-Boot-3-OpenAPI-Code-Gen-MVC-Demo</artifactId>
7 <version>1.0.0</version>
8 <packaging>jar</packaging>
9
10 <name>Spring-Boot-3-OpenAPI-Code-Gen-MVC-Demo</name>
11 <description>Spring-Boot-3-OpenAPI-Code-Gen-MVC-Demo</description>
12
13 <properties>
14 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15 <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
16 <java.version>17</java.version>
17 <javac.source>17</javac.source>
18 <javac.target>17</javac.target>
19 <maven.compiler.target>17</maven.compiler.target>
20 <maven.compiler.source>17</maven.compiler.source>
21
22 <start-class>code.MainApplication</start-class>
23 </properties>
24
25 <parent>
26 <groupId>org.springframework.boot</groupId>
27 <artifactId>spring-boot-starter-parent</artifactId>
28 <version>3.2.5</version>
29 </parent>
30
31 <dependencies>
32 <dependency>
33 <groupId>org.springframework.boot</groupId>
34 <artifactId>spring-boot-starter-web</artifactId>
35 </dependency>
36
37 <!-- Swagger UI -->
38 <dependency>
39 <groupId>org.springdoc</groupId>
40 <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
41 <version>2.5.0</version>
42 </dependency>
43
44 <!-- OpenAPI generator Maven plugin 會生成用到佢既 Java code -->
45 <dependency>
46 <groupId>org.openapitools</groupId>
47 <artifactId>jackson-databind-nullable</artifactId>
48 <version>0.2.6</version>
49 </dependency>
50
51 <dependency>
52 <groupId>net.logstash.logback</groupId>
53 <artifactId>logstash-logback-encoder</artifactId>
54 <version>7.4</version>
55 </dependency>
56
57 <dependency>
58 <groupId>org.apache.commons</groupId>
59 <artifactId>commons-lang3</artifactId>
60 </dependency>
61
62 <dependency>
63 <groupId>org.projectlombok</groupId>
64 <artifactId>lombok</artifactId>
65 <scope>provided</scope>
66 </dependency>
67 </dependencies>
68
69 <build>
70 <plugins>
71 <plugin>
72 <groupId>org.springframework.boot</groupId>
73 <artifactId>spring-boot-maven-plugin</artifactId>
74 <executions>
75 <execution>
76 <goals>
77 <goal>repackage</goal>
78 </goals>
79 </execution>
80 </executions>
81 </plugin>
82
83 <!-- OpenAPI generator Maven plugin -->
84 <plugin>
85 <groupId>org.openapitools</groupId>
86 <artifactId>openapi-generator-maven-plugin</artifactId>
87 <version>7.5.0</version>
88 <executions>
89 <execution>
90 <goals>
91 <goal>generate</goal>
92 </goals>
93 <configuration>
94 <inputSpec>${project.basedir}/src/main/resources/openapi.yml</inputSpec>
95 <generatorName>spring</generatorName>
96
97 <configOptions>
98 <apiPackage>code.api</apiPackage>
99 <modelPackage>code.model</modelPackage>
100 <useSpringBoot3>true</useSpringBoot3>
101
102 <!-- 我地可以用 delegatePattern -->
103 <delegatePattern>true</delegatePattern>
104
105 <!-- 或者用 interfaceOnly -->
106 <interfaceOnly>true</interfaceOnly>
107 </configOptions>
108 </configuration>
109 </execution>
110 </executions>
111 </plugin>
112 </plugins>
113 </build>
114</project>
註:
- OpenAPI generator Maven plugin 提供兩種生成 Java model code 既方式。
delegatePattern
interfaceOnly
2.2 寫 Swagger/OpenAPI Specification
因為係 API-first 既開發方式,所以我地寫 Java code 之前要先寫 Swagger/OpenAPI Specification 檔案。
src/main/resources/openapi.yml
:
1openapi: 3.0.0
2info:
3 version: 1.0.0
4 title: Spring-Boot-3-OpenAPI-Code-Gen-MVC-Demo
5 description: OpenAPI for Spring-Boot-3-OpenAPI-Code-Gen-MVC-Demo
6paths:
7 /users:
8 get:
9 operationId: getUsers
10 summary: Get all users
11 description: Get all users.
12 responses:
13 '200':
14 description: OK
15 content:
16 application/json:
17 schema:
18 type: array
19 items:
20 $ref: '#/components/schemas/UserDto'
21 post:
22 operationId: createUser
23 summary: Create user by ID
24 description: Create a user of the given ID.
25 requestBody:
26 required: true
27 description: User object (JSON).
28 content:
29 application/json:
30 schema:
31 $ref: '#/components/schemas/UserDto'
32 responses:
33 '200':
34 description: OK
35 content:
36 application/json:
37 schema:
38 $ref: '#/components/schemas/UserDto'
39 /users/{userId}:
40 get:
41 operationId: getUser
42 summary: Get user by ID
43 description: Get a user of the given ID.
44 parameters:
45 - name: userId
46 in: path
47 required: true
48 description: User ID.
49 schema:
50 type : long
51 responses:
52 '200':
53 description: OK
54 content:
55 application/json:
56 schema:
57 $ref: '#/components/schemas/UserDto'
58 put:
59 operationId: updateUser
60 summary: Update user by ID
61 description: Update a user of the given ID.
62 parameters:
63 - name: userId
64 in: path
65 required: true
66 description: User ID.
67 schema:
68 type : long
69 requestBody:
70 required: true
71 description: User object (JSON).
72 content:
73 application/json:
74 schema:
75 $ref: '#/components/schemas/UserDto'
76 responses:
77 '200':
78 description: OK
79 content:
80 application/json:
81 schema:
82 $ref: '#/components/schemas/UserDto'
83 delete:
84 operationId: deleteUser
85 summary: Delete user by ID
86 description: Delete a user of the given ID.
87 parameters:
88 - name: userId
89 in: path
90 required: true
91 description: User ID.
92 schema:
93 type : long
94 responses:
95 '200':
96 description: OK
97 content:
98 application/json:
99 schema:
100 $ref: '#/components/schemas/UserDto'
101components:
102 schemas:
103 UserDto:
104 type: object
105 properties:
106 id:
107 type: long
108 firstName:
109 type: string
110 lastName:
111 type: string
2.3 寫 Java code
無論係 interfaceOnly
或者係 delegatePattern
,都係用一樣既 main class。
MainApplication.java
:
1@SpringBootApplication
2public class MainApplication {
3
4 public static void main(String[] args) {
5 SpringApplication.run(MainApplication.class, args);
6 }
7}
2.3.1 interfaceOnly
版本
Project structure:
src/main/java
/code
MainApplication.java
UsersController.java
target/generated-sources/openapi/src/main/java
(由 OpenAPI generator Maven plugin 生成)
/code
/api
ApiUtil.java
UsersApi.java
/model
UsersController.java
:
1@Slf4j
2@RestController
3public class UsersController implements UsersApi {
4
5 @Override
6 public ResponseEntity<List<UserDto>> getUsers() {
7 log.info("getUsers");
8 return ResponseEntity.ok(Arrays.asList(randomUser(), randomUser()));
9 }
10
11 @Override
12 public ResponseEntity<UserDto> getUser(Long userId) {
13 log.info("getUser");
14 return ResponseEntity.ok(randomUser());
15 }
16
17 @Override
18 public ResponseEntity<UserDto> createUser(UserDto user) {
19 log.info("createUser");
20 return ResponseEntity.ok(user);
21 }
22
23 @Override
24 public ResponseEntity<UserDto> updateUser(Long userId, UserDto user) {
25 log.info("updateUser");
26 return ResponseEntity.ok(user);
27 }
28
29 @Override
30 public ResponseEntity<UserDto> deleteUser(Long userId) {
31 log.info("deleteUser");
32 return ResponseEntity.ok(randomUser());
33 }
34
35
36
37 private UserDto randomUser() {
38 final UserDto user = new UserDto();
39 user.setId(new Random().nextLong(100));
40 user.setFirstName(RandomStringUtils.randomAlphabetic(10));
41 user.setLastName(RandomStringUtils.randomAlphabetic(10));
42 return user;
43 }
44}
註:
UsersApi
係由 OpenAPI generator Maven plugin 生成既 Java interface。
UserDto
係由 OpenAPI generator Maven plugin 生成既 Java class。
2.3.2 delegatePattern
版本
Project structure:
src/main/java
/code
MainApplication.java
UsersApiDelegateImpl.java
target/generated-sources/openapi/src/main/java
(由 OpenAPI generator Maven plugin 生成)
/code
/api
ApiUtil.java
UsersApi.java
UsersApiController.java
UsersApiDelegate.java
/model
/org
(唔重要,因為我地唔會用裡面既野)
/openapitools
/configuration
HomeController.java
SpringDocConfiguration.java
OpenApiGeneratorApplication.java
RFC3339DateFormat.java
UsersApiDelegateImpl.java
:
1@Slf4j
2@Component
3public class UsersApiDelegateImpl implements UsersApiDelegate {
4
5 @Override
6 public ResponseEntity<List<UserDto>> getUsers() {
7 log.info("getUsers");
8 return ResponseEntity.ok(Arrays.asList(randomUser(), randomUser()));
9 }
10
11 @Override
12 public ResponseEntity<UserDto> getUser(Long userId) {
13 log.info("getUser");
14 return ResponseEntity.ok(randomUser());
15 }
16
17 @Override
18 public ResponseEntity<UserDto> createUser(UserDto user) {
19 log.info("createUser");
20 return ResponseEntity.ok(user);
21 }
22
23 @Override
24 public ResponseEntity<UserDto> updateUser(Long userId, UserDto user) {
25 log.info("updateUser");
26 return ResponseEntity.ok(user);
27 }
28
29 @Override
30 public ResponseEntity<UserDto> deleteUser(Long userId) {
31 log.info("deleteUser");
32 return ResponseEntity.ok(randomUser());
33 }
34
35
36
37 private UserDto randomUser() {
38 final UserDto user = new UserDto();
39 user.setId(new Random().nextLong(100));
40 user.setFirstName(RandomStringUtils.randomAlphabetic(10));
41 user.setLastName(RandomStringUtils.randomAlphabetic(10));
42 return user;
43 }
44}
註:
UsersApiDelegate
係由 OpenAPI generator Maven plugin 生成既 Java interface。
UserDto
係由 OpenAPI generator Maven plugin 生成既 Java class。
2.4 Application 配置
如果想改變 Swagger UI 既 endpoint paths,可以覆蓋默認既配置:
springdoc:
swagger-ui.path: "/api/swagger/swagger-ui.html" # 默認:/swagger-ui.html
api-docs.path: "/api/swagger/v3/api-docs" # 默認:/v3/api-docs
3 測試
3.1 cURL
列出所有 users:
curl -X GET http://localhost:8080/users
列出一個 user:
curl -X GET http://localhost:8080/users/1234
新增一個 user:
curl -X POST http://localhost:8080/users -H "Content-Type: application/json" -d "{ \"id\": 123, \"firstName\": \"Michael\", \"lastName\": \"Chung\" }"
更新一個 user:
curl -X PUT http://localhost:8080/users/1234 -H "Content-Type: application/json" -d "{ \"id\": 123, \"firstName\": \"Michael\", \"lastName\": \"Chung\" }"
刪除一個 user:
curl -X DELETE http://localhost:8080/users/1234
3.2 Swagger UI
- 喺瀏覽器打開 http://localhost:8080/swagger-ui.html。
- 佢會自動 redirect 去 http://localhost:8080/swagger-ui/index.html。
- 打開其中一個 API。
- 撳「Test it out」。
- 輸入必填既 request 資訊(如有)。
- Path variables
- Request parameters
- Request body
- 撳「Execute」。
3.3 取得 OpenAPI Specification 檔案
Swagger UI 亦會 expose 以下既 HTTP endpoints 畀我地拎到我地 microservice 既 OpenAPI Specification 檔案,我地只需要喺瀏覽器打開以下既 URLs:
4 筆記
4.1 OpenAPI generator Maven plugin 配置
configOptions 配置 | 解釋 |
---|
apiPackage | 喺邊個 Java package 去生成 Java API interfaces/classes。 |
modelPackage | 喺邊個 Java package 去生成 Java model classes。 |
useSpringBoot3 | 用黎控制生成既項目以及 Java code 係咪 Spring Boot 3.x 。如果係既話,會生成 jakarta 而唔係 javax package 既 import statements。 |
interfaceOnly | 會生成 Java API interface 畀我地 override 佢既所有 method body。 |
delegatePattern | 相比起 interfaceOnly ,會生成額外既 Spring controller class(@Controller )以及叫 delegate 既 Java interface 畀我地 override 佢既所有 method body。 |
4.1.1 OpenAPI generator Maven plugin - interfaceOnly
- 會生成 Java API interface。
- 每個 method 都會有
@RequestMapping
,return type 係 ResponseEntity
。
- 我地要 override 佢既所有 method body,否則默認會 return
HttpStatus.NOT_IMPLEMENTED
。
4.1.2 OpenAPI generator Maven plugin - delegatePattern
- 會生成叫 delegate 既 Java interface。
- 我地要 override 佢既所有 method body,否則默認會 return
HttpStatus.NOT_IMPLEMENTED
。
- 會生成 Java API interface。
- 每個 method 都會有
@RequestMapping
,return type 係 ResponseEntity
,默認會 return delegate 既 method 既 result。
- 會生成 Spring controller class。
- Class level 會有:
@Controller
@RequestMapping("${openapi.xxx.base-path:}")
(xxx
黎自個 OpenAPI Specification 既 info.title
field)。
- 佢 implement 左個 Java API interface,所以 Spring Boot 就會 expose 黎自個 Java API interface 既
@RequestMapping
既 HTTP endpoints。
- 會生成 main class 以及其他唔係太有用既 Java classes。
4.2 OpenAPI Specification fields
OpenAPI Specification field | 用途 |
---|
openapi | OpenAPI Specification 既版本號碼。 |
paths | 每個 expose 既 HTTP endpoint path 既配置。 |
HTTP endpoint path(e.g. /users ) | 裡面係佢支援既每個 HTTP request method 既配置。 |
operationId (e.g. getUsers ) | 會影響 reference 呢個 API operation 既 ID,對 OpenAPI generator Maven plugin 黎講會影響生成出黎既 Java method name。 |
schema | Data type,可以係 OpenAPI 本身支援 basic data types,可以用 $ref 黎 reference components.schemas 裡面既自定義 data types,亦可以係 array(再用 items field 表示 array elements 既 data type)。 |
requestBody | HTTP request body 既配置。 |
parameters | HTTP request body 以外既配置,例如 path variables(要用 in: path )、request parameters(亦叫 query strings,要用 in: query )、request headers(要用 in: header )。 |
response | HTTP response 每個支援既 HTTP status code 既配置。 |
components.schemas | 自定義既 data types,對 OpenAPI generator Maven plugin 黎講會影響生成出黎既 Java model classes。 |
5 參考資料