建立 RESTful API
如何在 Kotlin 中使用 Ktor 建立 RESTful API
程式碼範例: tutorial-server-restful-api
使用的外掛程式:
在本教學中,我們將解釋如何使用 Kotlin 和 Ktor 建置後端服務,其中包含一個會產生 JSON 檔案的 RESTful API 範例。
在
您將學習如何執行以下操作:
- 建立使用 JSON 序列化的 RESTful 服務。
- 了解 Content Negotiation的過程。ContentNegotiation 外掛程式有兩個主要目的:協商用戶端與伺服器之間的媒體類型,以及將內容序列化/反序列化為特定格式。
- 在 Ktor 中定義 REST API 的路由。
先決條件
您可以獨立進行本教學, 但我們強烈建議您先完成之前的教學,以學習如何
我們建議您安裝 IntelliJ IDEA,但您也可以使用其他您偏好的 IDE。
哈囉,RESTful 工作管理員
在本教學中,您將把現有的工作管理員重寫為 RESTful 服務。為此,您將使用多個 Ktor
雖然您可以手動將其新增到現有專案中,但產生一個新專案,然後逐步加入前一個教學的程式碼會更簡單。您將在過程中重新審視所有程式碼,因此不需要手邊備有前一個專案。
在 Project artifact 欄位中,輸入 com.example.ktor-rest-task-app 作為您的專案構件名稱。

在外掛程式區段中,搜尋並點擊 Add 按鈕來新增以下外掛程式:
- Content Negotiation
- kotlinx.serialization
- Static Content
新增外掛程式後,您將看到專案設定下方列出的所有外掛程式。 
點擊 Download 按鈕來產生並下載您的 Ktor 專案。
在 IntelliJ IDEA 中開啟您的專案,如之前的 在 IntelliJ IDEA 中開啟、探索並執行您的 Ktor 專案 教學所述。
導覽至 src/main/kotlin 並建立一個名為 model 的子套件。
在 model 套件中,建立一個新的 Task.kt 檔案。
開啟 Task.kt 檔案並新增一個
enum來表示優先級,以及一個class來表示任務:kotlin在前一個教學中,您使用擴充函式將
Task轉換為 HTML。而在這裡,Task類別標註了來自kotlinx.serialization程式庫的Serializable型別。開啟 Routing.kt 檔案,並將現有程式碼替換為下方的實作:
kotlin與之前的教學類似,您為指向 URL
/tasks的 GET 請求建立了一個路由。 這一次,您不再需要手動轉換任務清單,而是直接回傳該清單。在 IntelliJ IDEA 中,點擊執行按鈕 (
) 來啟動應用程式。
在瀏覽器中導覽至 http://0.0.0.0:8080/tasks。您應該會看到任務清單的 JSON 版本,如下所示:

顯然,背後已經為我們完成了很多工作。究竟發生了什麼事?
了解內容協商
透過瀏覽器進行內容協商
當您建立專案時,您包含了
在 HTTP 中,用戶端透過 Accept 標頭發出它可以呈現哪些內容類型的訊號。此標頭的值是一個或多個內容類型。在上述情況下,您可以透過使用瀏覽器內建的開發人員工具來檢查此標頭的值。
考慮以下範例:
請注意 */* 的包含。此標頭發出它接受 HTML、XML 或圖片的訊號,但也接受任何其他內容類型。
Content Negotiation 外掛程式需要找到一種格式來將資料傳回瀏覽器。如果您查看專案中產生的程式碼,您會在 src/main/kotlin 內找到一個名為 Serialization.kt 的檔案,其中包含以下內容:
這段程式碼安裝了 ContentNegotiation 外掛程式,同時也配置了 kotlinx.serialization 外掛程式。有了這個,當用戶端發送請求時,伺服器可以回傳序列化為 JSON 的物件。
在瀏覽器請求的情況下,ContentNegotiation 外掛程式知道它只能回傳 JSON,而瀏覽器會嘗試顯示發送給它的任何內容。所以請求成功了。
為了模擬這種情況,請開啟 src/main/resources/static 內的 index.html 頁面,並將預設內容替換為以下內容:
html此頁面包含一個 HTML 表單和一個空表格。在提交表單時,JavaScript 事件處理常式會向
/tasks端點發送請求,並將Accept標頭設置為application/json。回傳的資料隨後被反序列化並新增到 HTML 表格中。在 IntelliJ IDEA 中,點擊重新執行按鈕 (
) 以重啟應用程式。
導覽至 URL http://0.0.0.0:8080/static/index.html。您應該能夠透過點擊 View The Tasks 按鈕來獲取資料:

在生產環境中,您通常不會直接在瀏覽器中顯示 JSON。相反地,在瀏覽器中執行的 JavaScript 程式碼會發出請求,然後將回傳的資料作為單頁應用程式 (SPA) 的一部分進行顯示。通常,這種應用程式是使用像 React、 Angular 或 Vue.js 這樣的架構編寫的。
新增 GET 路由
既然您已經熟悉了內容協商的過程,請繼續將
重複使用任務存儲庫
您可以無需任何修改地重複使用任務存儲庫,所以讓我們首先執行此操作。
在 model 套件中建立一個新的 TaskRepository.kt 檔案。
開啟 TaskRepository.kt 並新增以下程式碼:
kotlin
重複使用 GET 請求的路由
既然您已經建立了存儲庫,就可以實作 GET 請求的路由。之前的程式碼可以簡化,因為您不再需要擔心將任務轉換為 HTML:
導覽至 src/main/kotlin 中的 Routing.kt 檔案。
使用以下實作更新
Application.configureRouting()函式內的/tasks路由程式碼:kotlin有了這個,您的伺服器可以回應以下 GET 請求:
/tasks回傳存儲庫中的所有任務。/tasks/byName/{taskName}回傳按指定的taskName過濾的任務。/tasks/byPriority/{priority}回傳按指定的priority過濾的任務。
在 IntelliJ IDEA 中,點擊重新執行按鈕 (
) 以重啟應用程式。
測試功能
您可以在瀏覽器中測試這些路由。例如,導覽至 http://0.0.0.0:8080/tasks/byPriority/Medium 以 JSON 格式查看所有優先級為 Medium 的任務:

鑑於這些類型的請求通常來自 JavaScript,更精細的測試更為理想。對此,您可以使用專門的工具,例如 Postman。
在 Postman 中,使用 URL 建立一個新的 GET 請求
http://0.0.0.0:8080/tasks/byPriority/Medium。在 Headers 面板中,將 Accept 標頭的值設置為
application/json。點擊 Send 發送請求,並在回應檢視器中查看回應。

在專案根目錄中,建立一個新的 REST Task Manager.http 檔案。
開啟 REST Task Manager.http 檔案並新增以下 GET 請求:
http要在 IntelliJ IDEA 中發送請求,請點擊其旁邊的裝訂邊圖示 (
)。
這將在 Services 工具視窗中開啟並執行:

在 IntelliJ IDEA Ultimate 中,您可以在 HTTP 請求檔案中執行相同的步驟。
NOTE
另一種測試路由的方法是在 Kotlin Notebook 中使用 khttp 程式庫。新增 POST 請求的路由
在前一個教學中,任務是透過 HTML 表單建立的。然而,由於您現在正在建置 RESTful 服務,您不再需要那樣做。相反地,您將利用 kotlinx.serialization 架構,它將承擔大部分繁重的工作。
開啟 src/main/kotlin 內的 Routing.kt 檔案。
向
Application.configureRouting()函式新增一個新的 POST 路由,如下所示:kotlin新增以下新匯入:
kotlin當向
/tasks發送 POST 請求時,會使用kotlinx.serialization架構將請求的主體轉換為Task物件。如果成功,任務將新增到存儲庫中。如果反序列化過程失敗,伺服器會處理SerializationException,而如果任務名稱重複,則會處理IllegalStateException。重啟應用程式。
要在 Postman 中測試此功能,請向 URL
http://0.0.0.0:8080/tasks建立一個新的 POST 請求。在 Body 面板中,新增以下 JSON 文件以表示新任務:
json
點擊 Send 發送請求。
您可以透過向 http://0.0.0.0:8080/tasks 發送 GET 請求來驗證任務是否已新增。
在 IntelliJ IDEA Ultimate 中,您可以透過將以下內容新增到您的 HTTP 請求檔案來執行相同的步驟:
http
新增移除支援
您即將完成向服務新增基本操作。這些操作通常被總結為 CRUD(建立 Create、讀取 Read、更新 Update 和刪除 Delete)操作。現在您將實作刪除操作。
在 TaskRepository.kt 檔案中,在
TaskRepository物件內新增以下方法以根據名稱移除任務:kotlin開啟 Routing.kt 檔案,並在
routing()函式中新增一個端點以處理 DELETE 請求:kotlin重啟應用程式。
將以下 DELETE 請求新增到您的 HTTP 請求檔案中:
http要在 IntelliJ IDEA 中發送 DELETE 請求,請點擊其旁邊的裝訂邊圖示 (
)。
您將在 Services 工具視窗中看到回應:

使用 Ktor Client 建立單元測試
到目前為止,您一直手動測試應用程式,但正如您已經注意到的,這種方法耗時且無法擴充。相反地,您可以實作
client 物件來獲取並反序列化 JSON。 開啟 src/test/kotlin 內的 ServerTest.kt 檔案。
將 ServerTest.kt 檔案的內容替換為以下內容:
kotlin請注意,您需要將
ContentNegotiation和kotlinx.serialization外掛程式安裝到 Plugins 中,就像在伺服器端所做的一樣。將以下相依性新增到您的 build.gradle.kts 檔案中:
kotlin
使用 JsonPath 建立單元測試
使用 Ktor client 或類似的程式庫測試服務固然方便,但從品質保證 (QA) 的角度來看,它有一個缺點。伺服器不直接處理 JSON,因此無法確定其對 JSON 結構的假設。
例如,諸如以下的假設:
- 當實際上使用
object時,值正被儲存在array中。 - 屬性正以
numbers儲存,而它們實際上是strings。 - 成員正按照宣告的順序進行序列化,而實際上並非如此。
如果您的服務旨在供多個用戶端使用,那麼對 JSON 結構有信心至關重要。為了實現這一點,請使用 Ktor Client 從伺服器檢索文本,然後使用 JSONPath 程式庫分析此內容。
在您的 build.gradle.kts 檔案中,將 JSONPath 程式庫新增到
dependencies區塊:kotlin導覽至 src/test/kotlin 資料夾並建立一個新的 ApplicationJsonPathTest.kt 檔案。
開啟 ApplicationJsonPathTest.kt 檔案並向其中新增以下內容:
kotlinJsonPath 查詢的工作原理如下:
$[*].name表示「將文件視為陣列,並回傳每個項目的 name 屬性值」。$[?(@.priority == '$priority')].name表示「回傳陣列中優先級等於提供值的所有項目的 name 屬性值」。
您可以使用類似這樣的查詢來確認您對回傳 JSON 的理解。當您進行程式碼重構和服務重新部署時,序列化中的任何修改都會被識別出來,即使它們沒有破壞當前架構的反序列化。這使您能夠充滿信心地重新發布公開可用的 API。
後續步驟
恭喜!您現在已經完成了為工作管理員應用程式建立 RESTful API 服務,並學習了使用 Ktor Client 和 JsonPath 進行單元測試的細節。
繼續閱讀
