前言:
遵循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等多個知識點高級進階乾貨。
領取方式:關注+轉發,私信回覆【資料】即可免費領取
閱讀更多 java架構北風 的文章