本教程介紹使用 Go 和Gin Web 框架(Gin)編寫 RESTful Web 服務 API 的基礎知識。
如果您對 Go 及其工具有基本的瞭解,您將從本教程中獲得最大收益。
如果這是您第一次接觸 Go,請參閱教程:Go 入門以獲取快速介紹。
Gin 簡化了與構建 Web 應用程式(包括 Web 服務)相關的許多編碼任務。
在本教程中,您將使用 Gin 來路由請求、檢索請求詳細資訊並編組 JSON 以獲取響應。
在本教程中,您將構建一個具有兩個端點的 RESTful API 伺服器。您的示例專案將是有關復古爵士樂唱片的資料儲存庫。
本教程包括以下部分:
設計 API 路徑。
為您的程式碼建立一個資料夾。
建立資料。
編寫一個處理程式來返回所有專案。
編寫一個處理程式來新增一個新專案。
編寫一個處理程式來返回一個特定的專案。
注意:
有關其他教程,請參閱教程。
先決條件
Go 1.16 或更高版本的安裝。
有關安裝說明,請參閱安裝 Go。
一種編輯程式碼的工具。
您擁有的任何文字編輯器都可以正常工作。
命令終端。
Go 適用於 Linux 和 Mac 上的任何終端,以及 Windows 中的 PowerShell 或 cmd。
curl工具。
在 Linux 和 Mac 上,這應該已經安裝了。在 Windows 上,它包含在 Windows 10 Insider build 17063 及更高版本中。對於較早的 Windows 版本,您可能需要安裝它。有關更多資訊,請參閱Tar 和 Curl 來到 Windows。
設計 API
您將構建一個 API,該 API 提供對銷售黑膠唱片老式唱片商店的訪問。
因此,您需要提供url,客戶端可以透過這些url為使用者獲取和新增專輯。
在開發 API 時,您通常從設計url開始。如果路徑易於理解,您的 API 使用者將獲得更多成功。
以下是您將在本教程中建立的端點。
/albums
– 獲取所有專輯的列表,以 JSON 形式返回。
– 從以 JSON 形式傳送的請求資料中新增新專輯。
/albums/:id
– 透過 ID 獲取專輯,以 JSON 形式返回專輯資料。
接下來,您將為您的程式碼建立一個資料夾。
為您的程式碼建立一個資料夾
首先,為您將編寫的程式碼建立一個專案。
開啟命令提示符並切換到您的主目錄。
在 Linux 或 Mac 上:
$ cd
在 Windows 上:
C:\> cd %HOMEPATH%
使用命令提示符為您的程式碼建立一個名為 web-service-gin 的目錄。
$ mkdir web-service-gin
$ cd web-service-gin
建立一個模組,您可以在其中管理依賴項。
執行該命令,為其指定程式碼所在模組的路徑。
$ go mod init example/web-service-gin
go: creating new go。mod: module example/web-service-gin
此命令建立一個 go。mod 檔案,您新增的依賴項將在其中列出以進行跟蹤。有關使用模組路徑命名模組的更多資訊,請參閱管理依賴項。
接下來,您將設計用於處理資料的資料結構。
建立資料
為使本教程簡單,您將資料儲存在記憶體中。更典型的 API 會與資料庫互動。
請注意,將資料儲存在記憶體中意味著每次停止伺服器時專輯集都會丟失,然後在啟動時重新建立。
編寫程式碼
使用您的文字編輯器,在 web-service 目錄中建立一個名為 main。go 的檔案。您將在此檔案中編寫 Go 程式碼。
在 main。go 檔案的頂部,貼上以下包宣告。
package main
獨立程式(與庫相對)始終在 package 中。
在包宣告下方,貼上以下結構宣告 。您將使用它在記憶體中儲存專輯資料。
結構標記,例如指定當結構的內容序列化為 JSON 時欄位的名稱應該是什麼。如果沒有它們,JSON 將使用結構體的大寫欄位名稱——這種樣式在 JSON 中並不常見。
// album represents data about a record album。
type album struct {
ID string `json:“id”`
Title string `json:“title”`
Artist string `json:“artist”`
Price float64 `json:“price”`
}
在您剛剛新增的結構宣告下方,貼上以下結構片段, 其中包含您將用於啟動的資料。
// albums slice to seed record album data。
var albums = []album{
,
,
,
}
接下來,您將編寫程式碼來實現您的第一個介面。
編寫一個處理程式來返回所有專案
當客戶端在 發出請求時,您希望以 JSON 形式返回所有專輯。
為此,您將編寫以下內容:
準備響應的邏輯
將請求路徑對映到邏輯的程式碼
請注意,這與它們在執行時的執行方式相反,但您首先新增依賴項,然後新增依賴它們的程式碼。
編寫程式碼
在上一節中新增的結構體程式碼下,貼上以下程式碼以獲取專輯列表。
此函式從結構切片建立 JSON ,將 JSON 寫入響應。
// getAlbums responds with the list of all albums as JSON。
func getAlbums(c *gin。Context) {
c。IndentedJSON(http。StatusOK, albums)
}
在此程式碼中,您:
編寫一個帶引數的函式 。請注意,您可以為該函式指定任何名稱——Gin 和 Go 都不需要特定的函式名稱格式。
是Gin最重要的部分。它攜帶請求詳細資訊、驗證和序列化 JSON 等。(儘管名稱相似,但這與 Go 的內建包不同。)
呼叫以將結構序列化為 JSON 並將其新增到響應中。
該函式的第一個引數是您要傳送給客戶端的 HTTP 狀態程式碼。在這裡,您從包中傳遞常量以指示。
請注意,您可以替換 為傳送更緊湊的 JSON的呼叫。在實踐中,縮排形式在除錯時更容易使用,並且大小差異通常很小。
在 main。go 的頂部附近,就在slice宣告的下方,貼上下面的程式碼以將處理程式函式分配給介面路徑。
這會建立一個關聯,在該關聯中處理對端點路徑的請求 。
func main() {
router := gin。Default()
router。GET(“/albums”, getAlbums)
router。Run(“localhost:8080”)
}
在此程式碼中,您:
使用。
使用該函式將HTTP 方法和路徑與處理程式函式相關聯。
請注意,你傳遞的名稱的的功能。這與傳遞函式的結果不同,傳遞函式是傳遞(注意括號)。
使用功能將路由器連線到一個並啟動伺服器。
在 main。go 的頂部附近,就在包宣告的下方,匯入支援剛剛編寫的程式碼所需的包。
第一行程式碼應該是這樣的:
package main
import (
“net/http”
“github。com/gin-gonic/gin”
)
儲存 main。go。
執行程式碼
您已經啟動了一個 API!在下一部分中,您將使用程式碼建立另一個url來處理新增專案的請求。
編寫一個處理程式來新增一個新專案
當客戶端在 處發出請求時,您希望將請求正文中描述的專輯新增到現有專輯資料中。
為此,您將編寫以下內容:
將新專輯新增到現有列表的邏輯。
將請求路由到您的邏輯的一些程式碼。
編寫程式碼
新增程式碼以將專輯資料新增到專輯列表中。
在語句之後的某處貼上以下程式碼。(檔案的末尾是這段程式碼的好地方,但 Go 不強制你宣告函式的順序。)
// postAlbums adds an album from JSON received in the request body。
func postAlbums(c *gin。Context) {
var newAlbum album
// Call BindJSON to bind the received JSON to
// newAlbum。
if err := c。BindJSON(&newAlbum); err != nil {
return
}
// Add the new album to the slice。
albums = append(albums, newAlbum)
c。IndentedJSON(http。StatusCreated, newAlbum)
}
在此程式碼中,您:
使用請求體結合。
將從 JSON 初始化的結構附加到slice。
向響應新增狀態程式碼,以及表示您新增的專輯的 JSON。
更改您的函式,使其包含該函式,如下所示。
func main() {
router := gin。Default()
router。GET(“/albums”, getAlbums)
router。POST(“/albums”, postAlbums)
router。Run(“localhost:8080”)
}
在此程式碼中,您:
將路徑中的方法與函式相關聯。
使用 Gin,您可以將處理程式與 HTTP 方法和路徑組合相關聯。透過這種方式,您可以根據客戶端使用的方法單獨路由傳送到單個路徑的請求。
執行程式碼
如果伺服器從上一節開始仍在執行,請停止它。
從包含 main。go 的目錄中的命令列,執行程式碼。
$ go run 。
從不同的命令列視窗,用於向正在執行的 Web 服務發出請求。
$ curl http://localhost:8080/albums \
——include \
——header “Content-Type: application/json” \
——request “POST” \
——data ‘{“id”: “4”,“title”: “The Modern Sound of Betty Carter”,“artist”: “Betty Carter”,“price”: 49。99}’
該命令應顯示新增專輯的標題和 JSON。
HTTP/1。1 201 Created
Content-Type: application/json; charset=utf-8
Date: Wed, 02 Jun 2021 00:34:12 GMT
Content-Length: 116
{
“id”: “4”,
“title”: “The Modern Sound of Betty Carter”,
“artist”: “Betty Carter”,
“price”: 49。99
}
與上一節一樣,使用檢索專輯的完整列表,您可以使用它來確認新專輯已新增。
$ curl http://localhost:8080/albums \
——header “Content-Type: application/json” \
——request “GET”
該命令應顯示專輯列表。
[
{
“id”: “1”,
“title”: “Blue Train”,
“artist”: “John Coltrane”,
“price”: 56。99
},
{
“id”: “2”,
“title”: “Jeru”,
“artist”: “Gerry Mulligan”,
“price”: 17。99
},
{
“id”: “3”,
“title”: “Sarah Vaughan and Clifford Brown”,
“artist”: “Sarah Vaughan”,
“price”: 39。99
},
{
“id”: “4”,
“title”: “The Modern Sound of Betty Carter”,
“artist”: “Betty Carter”,
“price”: 49。99
}
]
在下一部分中,您將新增程式碼來處理特定專案。
編寫處理程式以返回特定專案
當客戶端向 發出請求時,您希望返回 ID 與path 引數匹配的相簿。
為此,您將:
新增邏輯以檢索請求的專輯。
將路徑對映到邏輯。
編寫程式碼
在您在上一節中新增的函式下方,貼上以下程式碼以檢索特定專輯。
該函式將提取請求路徑中的ID,然後定位匹配的專輯。
// getAlbumByID locates the album whose ID value matches the id
// parameter sent by the client, then returns that album as a response。
func getAlbumByID(c *gin。Context) {
id := c。Param(“id”)
// Loop over the list of albums, looking for
// an album whose ID value matches the parameter。
for _, a := range albums {
if a。ID == id {
c。IndentedJSON(http。StatusOK, a)
return
}
}
c。IndentedJSON(http。StatusNotFound, gin。H{“message”: “album not found”})
}
在此程式碼中,您:
用於從 URL檢索路徑引數。當您將此處理程式對映到路徑時,您將在路徑中包含引數的佔位符。
迴圈切片中的結構,查詢欄位值與引數值匹配的結構。如果找到,則將該結構序列化為JSON 並將其作為帶有HTTP 程式碼的響應返回。
如上所述,現實世界的服務可能會使用資料庫查詢來執行此查詢。
如果找不到專輯,則返回 HTTP錯誤。
最後,更改您的使其包含對 的新呼叫,路徑現在為,如以下示例所示。
func main() {
router := gin。Default()
router。GET(“/albums”, getAlbums)
router。GET(“/albums/:id”, getAlbumByID)
router。POST(“/albums”, postAlbums)
router。Run(“localhost:8080”)
}
在此程式碼中,您:
將路徑與函式關聯。在 Gin 中,路徑中專案前面的冒號表示該專案是路徑引數。
執行程式碼
如果伺服器從上一節開始仍在執行,請停止它。
從包含 main。go 的目錄中的命令列,執行程式碼以啟動伺服器。
$ go run 。
從不同的命令列視窗,用於向正在執行的 Web 服務發出請求。
$ curl http://localhost:8080/albums/2
該命令應顯示您使用其 ID 的專輯的 JSON。如果未找到專輯,您將收到帶有錯誤訊息的 JSON。
{
“id”: “2”,
“title”: “Jeru”,
“artist”: “Gerry Mulligan”,
“price”: 17。99
}
結論
恭喜!您剛剛使用 Go 和 Gin 編寫了一個簡單的 RESTful Web 服務。
完整程式碼
本部分包含您使用本教程構建的應用程式的程式碼。
package main
import (
“net/http”
“github。com/gin-gonic/gin”
)
// album represents data about a record album。
type album struct {
ID string `json:“id”`
Title string `json:“title”`
Artist string `json:“artist”`
Price float64 `json:“price”`
}
// albums slice to seed record album data。
var albums = []album{
,
,
,
}
func main() {
router := gin。Default()
router。GET(“/albums”, getAlbums)
router。GET(“/albums/:id”, getAlbumByID)
router。POST(“/albums”, postAlbums)
router。Run(“localhost:8080”)
}
// getAlbums responds with the list of all albums as JSON。
func getAlbums(c *gin。Context) {
c。IndentedJSON(http。StatusOK, albums)
}
// postAlbums adds an album from JSON received in the request body。
func postAlbums(c *gin。Context) {
var newAlbum album
// Call BindJSON to bind the received JSON to
// newAlbum。
if err := c。BindJSON(&newAlbum); err != nil {
return
}
// Add the new album to the slice。
albums = append(albums, newAlbum)
c。IndentedJSON(http。StatusCreated, newAlbum)
}
// getAlbumByID locates the album whose ID value matches the id
// parameter sent by the client, then returns that album as a response。
func getAlbumByID(c *gin。Context) {
id := c。Param(“id”)
// Loop through the list of albums, looking for
// an album whose ID value matches the parameter。
for _, a := range albums {
if a。ID == id {
c。IndentedJSON(http。StatusOK, a)
return
}
}
c。IndentedJSON(http。StatusNotFound, gin。H{“message”: “album not found”})
}