【實戰技巧】使用Spring Boot和Swagger進行API優先開發

前言:

遵循API優先方法,我們在開始編碼之前先指定一個API。通過API描述語言,團隊可以進行協作而無需執行任何操作。

這些描述語言指定了端點,安全性模式,對象模式等。而且,大多數時候我們也可以生成這樣的規範代碼。

通常,API規範也成為該API的文檔。

API優先的好處

要開始進行組件或系統之間的集成,團隊需要簽訂合同。在我們的案例中,合同是API規範。API-first幫助團隊之間相互通信,而無需實現任何事情,它還使團隊可以並行工作。

API優先方法的亮點在於構建更好的API。僅關注需要提供的功能,簡約的API意味著需要維護的代碼更少。

使用Swagger編輯器創建API規範

讓我們在YAML文檔中創建自己的OpenAPI規範,為了更容易理解,我們將討論分為正在創建的YAML文檔的各個部分。

至於OpenAPI規範的更多詳細信息,可以訪問Github存儲庫。

一般信息

我們從文檔頂部的一些有關API的常規信息開始:

<code>openapi: 3.0.2
info:
title: Reflectoring
description: "Tutorials on Spring Boot and Java."
termsOfService: http://swagger.io/terms/
contact:
email: [email protected]
license:
name: Apache 2.0
url: http://www.apache.org/licenses/LICENSE-2.0.html
version: 0.0.1-SNAPSHOT
externalDocs:
description: Find out more about Reflectoring
url: https://reflectoring.io/about/
servers:
- url: https://reflectoring.swagger.io/v2
/<code>

該openapi字段允許我們定義文檔遵循的OpenAPI規範的版本。

在這一info部分中,我們添加了有關API的一些信息,這些字段應該是不言自明的。

最後,在servers節中,我們提供了實現API的服務器列表。

標籤

然後是關於我們的API的一些其他元數據:

<code>tags:
- name: user
description: Operations about user
externalDocs:
description: Find out more about our store
url: http://swagger.io
/<code>

本tags部分提供了其他元數據的字段,我們可以使用這些字段使我們的API更具可讀性並易於遵循,我們可以添加多個標籤,但是每個標籤都應該是唯一的。

路徑

接下來,先描述一些路徑,路徑保存有關單個端點及其操作的信息:

<code>paths:
/user/{username}:
get:
tags:
- user
summary: Get user by user name
operationId: getUserByName
parameters:
- name: username
in: path
description: 'The name that needs to be fetched. '
required: true
schema:
type: string
responses:
200:
description: successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/User'
404:
description: User not found
content: {}
/<code>

$ref字段允許我們引用自定義模式中的對象,在這種情況下,我們指的是User架構對象。

summary是該操作的簡短說明。

使用operationId,我們可以為操作定義唯一的標識符,我們可以將其視為我們的方法名稱。

最後,該responses對象允許我們定義操作的結果,我們必須為任何操作調用至少定義一個成功的響應代碼。

組件

本components節中全部介紹了API的對象,除非我們從組件對象外部的屬性中明確引用了它們,否則在組件對象中定義的對象將不會影響API,如上所述:

<code>components:
schemas:
User:
type: object
properties:
id:
type: integer
format: int64
username:
type: string
firstName:
type: string
... more attributes
userStatus:
type: integer
description: User Status
format: int32
securitySchemes:
reflectoring_auth:
type: oauth2
flows:
implicit:
authorizationUrl: http://reflectoring.swagger.io/oauth/dialog
scopes:
write:users: modify users
read:users: read users
api_key:
type: apiKey
name: api_key
in: header
/<code>

本schemas部分允許我們定義要在API中使用的對象。

在本securitySchemes節中,我們可以定義操作可以使用的安全性方案。

有兩種使用安全方案的可能方法:

首先,我們可以使用security字段將安全方案添加到特定操作:

<code>paths:
/user/{username}:
get:
tags:
- user
summary: Get user by user name
security:
- api_key: []
/<code>

在上面的示例中,我們明確指定使用api_key我們上面定義的方案保護路徑/ user / {username} 。

但是,如果我們要在整個項目中應用安全性,則只需將其指定為頂級字段即可:

<code>paths:
/user/{username}:
get:
tags:
- user
summary: Get user by user name
security:
- api_key: []
/<code>

現在,我們的所有路徑都應通過該api_key方案來保證。

從API規範生成代碼

定義了API之後,我們現在將根據上面的YAML文檔創建代碼。

我們將研究兩種不同的生成代碼的方法:

  • 使用Swagger編輯器手動生成代碼,以及
  • 使用OpenAPI Maven插件從Maven構建生成代碼。

先轉到Swagger Editor,然後將我們的YAML文件粘貼到其中。然後,我們從菜單中選擇“ 生成服務器”,然後選擇我們要生成哪種服務器(我使用“ Spring”)。

但是我並不 認為這是一個很好的方法!

首先,為我生成的代碼是使用Java 7和Spring Boot 1.5.22,它們都已經過時了。

其次,如果我們對規範進行更改(並且更改始終在發生),我們將不得不復制並粘貼手動更改的文件。

使用OpenAPI Maven插件生成代碼

更好的替代方法是使用OpenAPI Maven插件從Maven構建中生成代碼。

讓我們看一下文件夾結構,我選擇使用一個多模塊Maven項目,其中有兩個項目:

  • app,是根據我們的規範實現API的應用程序。
  • specification,其唯一的工作就是為我們的應用提供API規範。

文件夾結構如下所示:

<code>spring-boot-openapi
├── app
│ └── pom.xml
│ └── src
│ └── main
│ └── java
│ └── io.reflectoring
│ └── OpenAPIConsumerApp.java
├── specification
│ └── pom.xml
│ └── src
│ └── resources
│ └── openapi.yml
└── pom.xml
/<code>

為了簡單起見,我們省略了測試文件夾。

我們app是一個簡單的Spring啟動的項目,我們可以自動生成上start.spring.io,讓我們著眼於pom.xml從specification模塊,在那裡我們配置的OpenAPI Maven插件:

<code><plugin>
<groupid>org.openapitools/<groupid>
<artifactid>openapi-generator-maven-plugin/<artifactid>
<version>4.2.3/<version>
<executions>
<execution>

<goals>
<goal>generate/<goal>
/<goals>
<configuration>
${project.basedir}/src/main/resources/openapi.yml
/<inputspec>
<generatorname>spring/<generatorname>
<apipackage>io.reflectoring.api/<apipackage>
<modelpackage>io.reflectoring.model/<modelpackage>
<supportingfilestogenerate>
ApiUtil.java
/<supportingfilestogenerate>
<configoptions>
<delegatepattern>true/<delegatepattern>
/<configoptions>
/<configuration>
/<execution>
/<executions>
/<plugin>
/<code>

在本文中,我們使用spring生成器。

簡單地運行命令./mvnw install將生成實現我們的OpenAPI規範的代碼!

查看文件夾target/generated-sources/openapi/src/main/java/io/reflectoring/model,我們找到了User在YAML中定義的模型的代碼:

<code>@javax.annotation.Generated(...)
public class User {
@JsonProperty("id")
private Long id;

@JsonProperty("username")
private String username;

@JsonProperty("firstName")
private String firstName;

// ... more properties

@JsonProperty("userStatus")
private Integer userStatus;

// ... getters and setters

}
/<code>

生成器不僅生成模型,還生成端點,下面是生成的內容:

<code>public interface UserApiDelegate {

default Optional<nativewebrequest> getRequest() {
return Optional.empty();
}

/**
* POST /user : Create user
* Create user functionality
*
* @param body Created user object (required)
* @return successful operation (status code 200)
* @see UserApi#createUser
*/
default ResponseEntity<void> createUser(User body) {
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);

}
// ... omit deleteUser, getUserByName and updateUser
}
/<void>/<nativewebrequest>/<code>

當然,生成器無法為我們生成我們的業務邏輯,但是它確實會UserApiDelegate為我們實現上述接口。

它還創建一個UserApi接口,將調用委託給UserApiDelegate:

<code>@Validated
@Api(value = "user", description = "the user API")
public interface UserApi {

default UserApiDelegate getDelegate() {
return new UserApiDelegate() {};
}

/**
* POST /user : Create user

* Create user functionality
*
* @param body Created user object (required)
* @return successful operation (status code 200)
*/
@ApiOperation(value = "Create user",
nickname = "createUser",
notes = "Create user functionality",
tags={ "user", })
@ApiResponses(value = {
@ApiResponse(code = 200, message = "successful operation") })
@RequestMapping(value = "/user",
method = RequestMethod.POST)
default ResponseEntity<void> createUser(
@ApiParam(value = "Created user object" ,required=true )
@Valid
@RequestBody User body) {
return getDelegate().createUser(body);
}

// ... other methods omitted
}
/<void>/<code>

生成器還為我們創建了一個Spring控制器,用於實現UserApi接口:

<code>@javax.annotation.Generated(...)
@Controller
@RequestMapping("${openapi.reflectoring.base-path:/v2}")
public class UserApiController implements UserApi {

private final UserApiDelegate delegate;

public UserApiController(
@Autowired(required = false) UserApiDelegate delegate) {
this.delegate = Optional.ofNullable(delegate)
.orElse(new UserApiDelegate() {});
}

@Override
public UserApiDelegate getDelegate() {
return delegate;
}
}
/<code>

如果Spring UserApiDelegate在應用程序上下文中找到我們的實現,它將注入到控制器的構造函數中。否則,將使用默認實現。

讓我們啟動應用程序並點擊GET端點/v2/user/{username}。

<code>curl -I http://localhost:8080/v2/user/Petros
HTTP/1.1 501
Content-Length: 0
/<code>

但是為什麼我們會收到501響應(未實現)?

因為我們沒有實現UserApiDelegate接口,所以UserApiController使用了默認接口,該接口 返回HttpStatus.NOT_IMPLEMENTED。

現在讓我們實現UserApiDelegate:

<code>@Service
public class UserApiDelegateImpl implements UserApiDelegate {

@Override
public ResponseEntity<user> getUserByName(String username) {
User user = new User();
user.setId(123L);
user.setFirstName("Petros");

// ... omit other initialization

return ResponseEntity.ok(user);
}
}
/<user>/<code>

在類中添加@Service或@Component批註很重要,以便Spring可以將其拾取並注入到中UserApiController。

如果現在curl http://localhost:8080/v2/user/Petros再次運行,將收到有效的JSON響應:

<code>{
"id": 123,
"firstName": "Petros",

// ... omit other properties
}
/<code>

這UserApiDelegate是唯一的真理,這使我們能夠快速更改API。例如,如果我們更改規範並再次生成它,則只需實現新生成的方法。

如果我們不去實現它們,我們的應用程序將不會中斷。默認情況下,這些端點將返回HTTP狀態501(未實現)。

我認為,使用Maven插件而不是Swagger Editor生成OpenAPI規範是更好的選擇。那是因為對我們的選擇有更多的控制權,該插件提供了一些配置和使用Git作為版本控制工具,我們可以放心地查看在任的任何變化pom.xml和openapi.yml。

結論

使用OpenAPI,我們可以創建一個API規範,我們可以在團隊之間共享以交流合同。OpenAPI Maven插件使我們可以根據這樣的規範為Spring Boot生成樣板代碼,因此我們只需要自己實現業務邏輯即可。

讀者福利

分享一份整理好的Java學習資料,裡面包含了:分佈式架構、高可擴展、高性能、高併發、Jvm性能調優、Spring,MyBatis,Nginx源碼分析,Redis,ActiveMQ、、Mycat、Netty、Kafka、Mysql、Zookeeper、Tomcat、Docker、Dubbo、Nginx等多個知識點高級進階乾貨。

【實戰技巧】使用Spring Boot和Swagger進行API優先開發

【實戰技巧】使用Spring Boot和Swagger進行API優先開發

領取方式:關注+轉發,私信回覆【資料】即可免費領取


分享到:


相關文章: