作者:Tony Tam
Swagger Inflector 是 Swagger 團隊為在 JVM 上編寫 API 而開發的新專案。它目前處於預覽狀態,但正在積極開發和支援,作為在 Java 中編寫 API 的另一種以設計優先的方式。讓我們逐步了解如何使用 Inflector 專案。
[embed]http://www.slideshare.net/Swagger-API/the-inflector-project[/embed]
開始使用
首先,我們從 Swagger 規格開始。您可以在您喜愛的文字編輯器中以 JSON 或 YAML 建立規格,或使用 http://editor.swagger.io 上的絕佳線上編輯器來建構您的 API 定義。不用擔心進行變更!我們將逐步說明 Inflector 如何協助您快速迭代您的設計,而無需編寫任何程式碼。
在此示範中,我們假設您已從 此處 複製 Swagger 描述。
接下來,我們建立一個 YAML 檔案來設定 Inflector。這允許許多選項,但現在,讓我們只放置 Swagger 描述的位置
# inflector.yaml
swaggerUrl: ./src/main/swagger/swagger.yaml
由於 Inflector 在底層使用 Swagger Parser,因此您可以指向本機或遠端檔案。如果您正在託管您的定義,只需指定 http
或 https
協定,Swagger Parser 就會擷取它。此功能有一些非常有趣的用例,我們將在後續的部落格文章中介紹。
現在讓我們新增 Inflector 相依性。在執行階段,Inflector 只會使用 Jersey 2.6 作為 REST 框架,因此有很多用於生產整合的選項。現在,讓我們使用 Jetty Maven 外掛程式並新增 Inflector 作為相依性
<build>
<plugins>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.2.9.v20150224</version>
<configuration>
<monitoredDirName>.</monitoredDirName>
<scanTargets>
<scanTarget>inflector.yaml</scanTarget>
<scanTarget>src/main/swagger/swagger.yaml</scanTarget>
</scanTargets>
<scanIntervalSeconds>1</scanIntervalSeconds>
<webApp>
<contextPath>/</contextPath>
</webApp>
<httpConnector>
<port>8080</port>
<idleTimeout>60000</idleTimeout>
</httpConnector>
</configuration>
</plugin>
<!-- 您的其他外掛程式 -->
</plugins>
</build>
<dependencies>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-inflector</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<!-- 您的其他相依性 -->
查看範例 pom.xml,其中包含您需要的一切。
請注意,我們在外掛程式組態中設定了兩個掃描目標。這會告訴 Jetty,如果 ./inflector.yaml
或 src/main/swagger/swagger.yaml
描述檔案變更,則重新啟動應用程式。您會看到這如何讓使用 Inflector 進行開發變得輕鬆。
最後,讓我們新增一個 web.xml
。這讓我們可以在 Jersey 環境中,於服務的根目錄上掛載 Inflector 應用程式。我們還將新增 CORS 篩選器,以便我們可以輕鬆地從 Swagger UI 讀取 Swagger API。
<!-- from https://github.com/swagger-api/swagger-inflector/blob/master/samples/jetty-webxml/src/main/webapp/WEB-INF/web.xml -->
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<servlet>
<servlet-name>swagger-inflector</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>io.swagger.inflector.SwaggerInflector</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>swagger-inflector</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>CORSFilter</filter-name>
<filter-class>io.swagger.inflector.utils.CORSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CORSFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
請注意我們如何新增 CORS 篩選器。這就像任何標準的 web.xml,您可以新增篩選器、靜態 Servlet 等。這裡沒有任何魔法!
這就是我們的專案設定。現在讓我們開始使用它。
啟動伺服器
mvn package jetty:run
並在連接埠 8080
上讀取 Swagger 清單 (在 Jetty 外掛程式中設定)。根據您在 Swagger 描述中的 basePath
中設定的內容,您現在可以在 http://localhost:8080/{basePath}/swagger.json
開啟 Swagger 描述 (當然,根據規格,可以省略 basePath)。這會以 JSON 格式顯示您一直在編輯的規格。我知道,沒什麼大不了,但我們才剛開始。
請注意,根據我們的 Jetty 外掛程式組態,您可以將變更儲存到您的 swagger.yaml
檔案,並進行自動重新載入 (每秒掃描一次)。一個重點!如果您撰寫無效的規格,伺服器將拒絕提供它。使用 Swagger Editor 來取得撰寫有效 Swagger 描述的內容相關說明!
現在是有趣的部分。讓我們從我們的描述中取出一個路由
/pet/{petId}:
get
tags
- pet
summary: 依 ID 尋找寵物
description: 傳回單一寵物
operationId: getPetById
consumes
- application/x-www-form-urlencoded
produces
- application/xml
- application/json
parameters
- name: petId
in: path
description: 要傳回的寵物 ID
required: true
type: integer
format: int64
responses
"200":
description: 成功操作
schema
$ref: "#/definitions/Pet"
"400":
description: 提供無效的 ID
"404":
description: 找不到寵物
如果您點擊此端點 (http://localhost:8080/v2/pet/1
),Inflector 將根據您的 Accepts
標頭,以 JSON 或 XML 格式提供 #/definitions/Pet
模型的範例酬載。不需要程式碼!Inflector 會從 Swagger 結構描述建立範例
{
"id": 0,
"category": {
"id": 0,
"name": "string"
},
"name": "doggie",
"photoUrls": [
"string"
],
"tags": [
{
"id": 0,
"name": "string"
}
],
"status": "string"
}
這很有趣!您可以使用 Swagger 描述中的每個操作嘗試相同的操作。Inflector 將讓模擬服務變得簡單。同樣地,當您修改服務描述並進行變更時,Jetty 外掛程式會重新載入並立即顯示您的變更。您甚至可以載入 swagger-ui 並指向我們的 Swagger 描述來查看實際情況。
現在,讓我們跳過 Hello World 並讓它更真實。假設您不想要模擬回應 - 您想要實作一個控制器並放入一些業務邏輯。Inflector 將透過將請求直接路由到控制器來簡化此操作。
這是透過幾種技術設定的。讓我們逐步了解選項。
明確命名控制器 + 套件
您可以使用 Swagger 的 Extension
在描述中加入額外的元數據。在此用例中,我們將加入要將請求發送到的控制器類別名稱
/pet:
post
tags
- pet
summary: 將新的寵物添加到商店
operationId: addPet
# 我們正在配置 Inflector 以在此類別中尋找我們的控制器!
x-swagger-router-controller: io.swagger.sample.SampleController
現在在啟動時,Inflector 會在 io.swagger.sample.SampleController
類別中尋找符合此方法的方法。但我們的簽名是什麼???
Inflector 將使用 operationId
作為方法名稱。如果不存在,則會有一個演算法來建立一個(您應該直接指定它以簡化生活)。然後它會尋找符合操作輸入參數的參數簽名,外加一個用於內容的額外參數。事實上,如果您將應用程式配置為以偵錯記錄啟動,您會看到它試圖尋找什麼
DEBUG i.s.i.SwaggerOperationController - looking for operation: addPet(io.swagger.inflector.models.RequestContext request, com.fasterxml.jackson.databind.JsonNode body)
請注意,RequestContext
作為第一個參數——它包含有關標頭、媒體類型等的重要資訊。查看原始碼以查看它為您提供的內容。
請注意,body
直接對應到 JsonNode
物件。這會為您提供一個 Map
或 Array
類型物件,以作為您的網域模型(這也適用於 XML)。
自動定位命名控制器 + 套件
對於勇敢的人,您可以配置 Inflector 掃描 controllerPackage
# inflector.yaml
controllerPackage: io.swagger.sample.controllers
Inflector 將永遠在此處尋找。您也可以使用 controllerPackage
設定預設套件查找,並設定 x-swagger-router-controller: SampleController
,而不是使用 FQ 名稱。
然後,inflector 將使用第一個標籤來建構類別名稱。如果您有一個標記的操作
tags:
- pet
Inflector 現在將採用 controllerPackage
+ ‘.’ + Capitalize(tags[0]) + Controller
作為預期的類別名稱
io.swagger.sample.controllers.PetController
實作商業邏輯
實作商業邏輯就像在我們的控制器中建立該方法一樣簡單
package io.swagger.sample;
import io.swagger.inflector.models.RequestContext;
import io.swagger.inflector.models.ResponseContext;
import com.fasterxml.jackson.databind.JsonNode;
import javax.ws.rs.core.Response.Status;
public class SampleController {
// ResponseContext 是可選的,但它提供了關於寫入客戶端的額外控制
public ResponseContext addPet(RequestContext request, JsonNode body) {
return new ResponseContext()
.status(Status.OK)
.entity(body);
}
}
是的,很無聊,但這展示了我們現在如何完全控制回應物件,以及 Inflector 如何直接對應到我們的控制器!當您不需處理所有管道時,您會發現編寫 REST API 的速度有多快,這真是太棒了!
對應 POJO
假設我們想要一個具體的物件,而不是使用 JsonNode
。這很容易做到——您有兩種選擇。
首先,您可以使用 modelMappings
語法直接在 inflector.yaml
中對應模型
modelMappings:
Pet: io.swagger.sample.models.Pet
您猜對了,當我們在 Swagger 描述中看到 Pet
定義時,我們將使用 io.swagger.sample.models.Pet
實作。
其次,您可以設定 modelPackage
,Inflector 會嘗試尋找它
modelPackage: io.swagger.sample.models
這會導致 Inflector 從類別載入器載入 io.swagger.sample.models.Pet
。如果這兩種情況都存在,您的方法簽名會看起來更友善
DEBUG i.s.i.SwaggerOperationController - looking for operation: addPet(io.swagger.inflector.models.RequestContext request, io.swagger.sample.models.Pet body)
現在您可以開始使用看起來更熟悉的程式碼
public ResponseContext addPet(RequestContext request, Pet body) {
/* 您所有的商業邏輯 */
}
結論
這是一篇相當長的文章,但希望 Inflector 的設計優先目標很明確。一旦您開始編寫 API 而無需擔心所有的管道,您會發現您將建立更好的 API,而且速度更快。Inflector 消除了產生 Swagger 描述所需的註解和其他連接,並將 Swagger 本身置於 REST API 的 DSL 角色。