基於Go語言構建RESTful API服務
在實際開發項目中,你編寫的服務可以被其他服務使用,這樣就組成瞭微服務的架構;也可以被前端調用,這樣就可以前後端分離。那麼,本文主要介紹什麼是 RESTful API,以及 Go 語言是如何玩轉 RESTful API 的。
什麼是 RESTful API
RESTful API 是一套規范,它可以規范我們如何對服務器上的資源進行操作。在瞭解 RESTful API 之前,我先為你介紹下 HTTP Method,因為 RESTful API 和它是密不可分的。
說起 HTTP Method,最常見的就是POST和GET,其實最早在 HTTP 0.9 版本中,隻有一個GET方法,該方法是一個冪等方法,用於獲取服務器上的資源,也就是我們在瀏覽器中直接輸入網址回車請求的方法。
在 HTTP 1.0 版本中又增加瞭HEAD和POST方法,其中常用的是 POST 方法,一般用於給服務端提交一個資源,導致服務器的資源發生變化。
隨著網絡越來越復雜,發現這兩個方法是不夠用的,就繼續新增瞭方法。所以在 HTTP1.1 版本的時候,一口氣增加到瞭 9 個,新增的方法有 HEAD、OPTIONS、PUT、DELETE、TRACE、PATCH 和 CONNECT。下面我為你一一介紹它們的作用。
- GET 方法可請求一個指定資源的表示形式,使用 GET 的請求應該隻被用於獲取數據。
- HEAD 方法用於請求一個與 GET 請求的響應相同的響應,但沒有響應體。
- POST 方法用於將實體提交到指定的資源,通常導致服務器上的狀態變化或副作用。
- PUT 方法用於請求有效載荷替換目標資源的所有當前表示。
- DELETE 方法用於刪除指定的資源。
- CONNECT 方法用於建立一個到由目標資源標識的服務器的隧道。
- OPTIONS 方法用於描述目標資源的通信選項。
- TRACE 方法用於沿著到目標資源的路徑執行一個消息環回測試。
- PATCH 方法用於對資源應用部分修改。
從以上每個方法的介紹可以看到,HTTP 規范針對每個方法都給出瞭明確的定義,所以我們使用的時候也要盡可能地遵循這些定義,這樣我們在開發中才可以更好地協作。
理解瞭這些 HTTP 方法,就可以更好地理解 RESTful API 規范瞭,因為 RESTful API 規范就是基於這些 HTTP 方法規范我們對服務器資源的操作,同時規范瞭 URL 的樣式和 HTTP Status Code。
在 RESTful API 中,使用的主要是以下五種 HTTP 方法:
- GET,表示讀取服務器上的資源;
- POST,表示在服務器上創建資源;
- PUT,表示更新或者替換服務器上的資源;
- DELETE,表示刪除服務器上的資源;
- PATCH,表示更新 / 修改資源的一部分。
以上 HTTP 方法在 RESTful API 規范中是一個操作,操作的就是服務器的資源,服務器的資源通過特定的 URL 表示。
現在我們通過一些示例讓你更好地理解 RESTful API,如下所示:
HTTP GET http://localhost:1000/users HTTP GET http://localhost:1000/user/123
以上是兩個 GET 方法的示例:
- 第一個表示獲取所有用戶的信息
- 第二個表示獲取 ID 為 123 用戶的信息
下面再看一個 POST 方法的示例,如下所示:
HTTP POST http://localhost:1000/user
這個示例表示創建一個用戶,通過 POST 方法給服務器提供創建這個用戶所需的全部信息。
現在你已經知道瞭如何創建一個用戶,那麼如果要更新某個特定的用戶怎麼做呢?其實也非常簡單,示例代碼如下所示:
HTTP PUT http://localhost:1000/user/123
這表示要更新 / 替換 ID 為 123 的這個用戶,在更新的時候,會通過 PUT 方法提供更新這個用戶需要的全部用戶信息。這裡 PUT 方法和 POST 方法不太一樣的是,從 URL 上看,PUT 方法操作的是單個資源,比如這裡 ID 為 123 的用戶。
看到這裡,相信你已經知道瞭如何刪除一個用戶,示例代碼如下所示:
HTTP DELETE http://localhost:1000/user/123
DELETE 方法的使用和 PUT 方法一樣,也是操作單個資源,這裡是刪除 ID 為 123 的這個用戶。
一個簡單的 RESTful API
相信你已經非常瞭解什麼是 RESTful API 瞭,現在開始,我會帶你通過一個使用 Golang 實現 RESTful API 風格的示例,加深 RESTful API 的理解。
Go 語言的一個很大的優勢,就是可以很容易地開發出網絡後臺服務,而且性能快、效率高。在開發後端 HTTP 網絡應用服務的時候,我們需要處理很多 HTTP 的請求訪問,比如常見的RESTful API 服務,就要處理很多 HTTP 請求,然後把處理的信息返回給使用者。對於這類需求,Golang 提供瞭內置的 net/http 包幫我們處理這些 HTTP 請求,讓我們可以比較方便地開發一個 HTTP 服務。
下面我們來看一個簡單的 HTTP 服務的 Go 語言實現,代碼如下所示:
func main() { http.HandleFunc("/users",handleUsers) http.ListenAndServe(":1000", nil) } func handleUsers(w http.ResponseWriter, r *http.Request){ fmt.Fprintln(w,"ID:1,Name:張三") fmt.Fprintln(w,"ID:2,Name:李四") fmt.Fprintln(w,"ID:3,Name:王五") }
這個示例運行後,你在瀏覽器中輸入http://localhost:1000/users, 就可以看到如下內容信息:
ID:1,Name:張三
ID:2,Name:李四
ID:3,Name:王五
也就是獲取所有的用戶信息,但是這並不是一個 RESTful API,因為使用者不僅可以通過 HTTP GET 方法獲得所有的用戶信息,還可以通過 POST、DELETE、PUT 等 HTTP 方法獲得所有的用戶信息,這顯然不符合 RESTful API 的規范。
現在我對以上示例進行修改,使它符合 RESTful API 的規范,修改後的示例代碼如下所示:
func handleUsers(w http.ResponseWriter, r *http.Request){ switch r.Method { case "GET": w.WriteHeader(http.StatusOK) fmt.Fprintln(w,"ID:1,Name:張三") fmt.Fprintln(w,"ID:2,Name:李四") fmt.Fprintln(w,"ID:3,Name:王五") default: w.WriteHeader(http.StatusNotFound) fmt.Fprintln(w,"not found") } }
這裡我隻修改瞭 handleUsers 函數,在該函數中增加瞭隻在使用 GET 方法時,才獲得所有用戶的信息,其他情況返回 not found。
現在再運行這個示例,會發現隻能通過 HTTP GET 方法進行訪問瞭,使用其他方法會提示 not found。
RESTful JSON API
在項目中最常見的是使用 JSON 格式傳輸信息,也就是我們提供的 RESTful API 要返回 JSON 內容給使用者。
同樣用上面的示例,我把它改造成可以返回 JSON 內容的方式,示例代碼如下所示:
//數據源,類似MySQL中的數據 var users = []User{ {ID: 1,Name: "張三"}, {ID: 2,Name: "李四"}, {ID: 3,Name: "王五"}, } func handleUsers(w http.ResponseWriter, r *http.Request){ switch r.Method { case "GET": users,err:=json.Marshal(users) if err!=nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprint(w,"{\"message\": \""+err.Error()+"\"}") }else { w.WriteHeader(http.StatusOK) w.Write(users) } default: w.WriteHeader(http.StatusNotFound) fmt.Fprint(w,"{\"message\": \"not found\"}") } } //用戶 type User struct { ID int Name string }
從以上代碼可以看到,這次的改造主要是新建瞭一個 User 結構體,並且使用 users 這個切片存儲所有的用戶,然後在 handleUsers 函數中把它轉化為一個 JSON 數組返回。這樣,就實現瞭基於 JSON 數據格式的 RESTful API。
運行這個示例,在瀏覽器中輸入http://localhost:1000/users,可以看到如下信息:
[{“ID”:1,”Name”:”張三”},{“ID”:2,”Name”:”李四”},{“ID”:3,”Name”:”王五”}]
這已經是 JSON 格式的用戶信息,包含瞭所有用戶。
Gin 框架
雖然 Go 語言自帶的 net/http 包,可以比較容易地創建 HTTP 服務,但是它也有很多不足:
- 不能單獨地對請求方法(POST、GET 等)註冊特定的處理函數
- 不支持 Path 變量參數
- 不能自動對 Path 進行校準
- 性能一般
- 基於以上這些不足,出現瞭很多 Golang Web 框架,如 Mux,Gin、Fiber 等,今天我要為你介紹的就是這款使用最多的 Gin 框架。
引入 Gin 框架
Gin 框架是一個在 Github 上開源的 Web 框架,封裝瞭很多 Web 開發需要的通用功能,並且性能也非常高,可以讓我們很容易地寫出 RESTful API。
Gin 框架其實是一個模塊,也就是 Go Mod,所以采用 Go Mod 的方法引入即可。首先需要下載安裝 Gin 框架,安裝代碼如下:
go get -u github.com/gin-gonic/gin
然後就可以在 Go 語言代碼中導入使用瞭,導入代碼如下:
import "github.com/gin-gonic/gin"
通過以上安裝和導入這兩個步驟,就可以在你的 Go 語言項目中使用 Gin 框架瞭。
使用 Gin 框架
現在,已經引入瞭 Gin 框架,下面我就是用 Gin 框架重寫上面的示例,修改的代碼如下所示:
func main() { r:=gin.Default() r.GET("/users", listUser) r.Run(":1000") } func listUser(c *gin.Context) { c.JSON(200,users) }
相比 net/http 包,Gin 框架的代碼非常簡單,通過它的 GET 方法就可以創建一個隻處理 HTTP GET 方法的服務,而且輸出 JSON 格式的數據也非常簡單,使用 c.JSON 方法即可。
最後通過 Run 方法啟動 HTTP 服務,監聽在 1000 端口。現在運行這個 Gin 示例,在瀏覽器中輸入http://localhost:1000/users,看到的信息和通過 net/http 包實現的效果是一樣的。
新增一個用戶
現在你已經可以使用 Gin 獲取所有用戶,還可以獲取特定的用戶,那麼你也應該知道如何新增一個用戶瞭,現在我通過 Gin 實現如何新增一個用戶,看和你想的方案是否相似。
根據 RESTful API 規范,實現新增使用的是 POST 方法,並且 URL 的格式為http://localhost:1000/users,向這個 URL 發送數據,就可以新增一個用戶,然後返回創建的用戶信息。
現在我使用 Gin 框架實現新增一個用戶,示例代碼如下:
func main() { //省略沒有改動的代碼 r.POST("/users", createUser) } func createUser(c *gin.Context) { name := c.DefaultPostForm("name", "") if name != "" { u := User{ID: len(users) + 1, Name: name} users = append(users, u) c.JSON(http.StatusCreated,u) } else { c.JSON(http.StatusOK, gin.H{ "message": "請輸入用戶名稱", }) } }
以上新增用戶的主要邏輯是獲取客戶端上傳的 name 值,然後生成一個 User 用戶,最後把它存儲到 users 集合中,達到新增用戶的目的。
在這個示例中,使用 POST 方法來新增用戶,所以隻能通過 POST 方法才能新增用戶成功。
現在運行這個示例,然後通過如下命令發送一個新增用戶的請求,查看結果:
➜ curl -X POST -d ‘name=Ele’ http://localhost:1000/users
{“ID”:4,”Name”:”Ele”}
可以看到新增用戶成功,並且返回瞭新增的用戶,還有分配的 ID。
獲取特定的用戶
現在你已經掌握瞭如何使用 Gin 框架創建一個簡單的 RESTful API,並且可以返回所有的用戶信息,那麼如何獲取特定用戶的信息呢?
下面我通過 Gin 框架 Path 路徑參數來實現獲得特定用戶的信息功能,示例代碼如下:
func main() { //省略沒有改動的代碼 r.GET("/users/:id", getUser) } func getUser(c *gin.Context) { id := c.Param("id") var user User found := false //類似於數據庫的SQL查詢 for _, u := range users { if strings.EqualFold(id, strconv.Itoa(u.ID)) { user = u found = true break } } if found { c.JSON(200, user) } else { c.JSON(404, gin.H{ "message": "用戶不存在", }) } }
在 Gin 框架中,路徑中使用冒號表示 Path 路徑參數,比如示例中的 :id,然後在 getUser 函數中可以通過 c.Param(“id”) 獲取需要查詢用戶的 ID 值。PS:Param 方法的參數要和 Path 路徑參數中的一致,比如示例中都是 ID。
現在運行這個示例,通過瀏覽器訪問http://localhost:1000/users/4,就可以獲得 ID 為 4 的用戶,輸出信息如下所示:
{“ID”:4,”Name”:”Ele”}
可以看到,已經正確的獲取到瞭 ID 為 4 的用戶,他的名字叫Ele。
假如我們訪問一個不存在的 ID,會得到什麼結果呢?比如 99,示例如下所示:
curl http://localhost:1000/users/99
{“message”:”用戶不存在”}
從以上示例輸出可以看到,返回瞭『用戶不存在』的信息,和我們代碼中處理的邏輯一樣。
總結
Go 語言已經給我們提供瞭比較強大的 SDK,讓我們可以很容易地開發網絡服務的應用,而借助第三方的 Web 框架,可以讓這件事情更容易、更高效。比如這篇文章介紹的 Gin 框架,就可以很容易讓我們開發出 RESTful API。在我們做項目開發的時候,要善於借助已經有的輪子,讓自己的開發更有效率,也更容易實現。
到此這篇關於基於Go語言構建RESTful API服務的文章就介紹到這瞭,更多相關Go語言構建RESTful API 內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- golang微服務框架基礎Gin基本路由使用詳解
- 關於SpringMVC對Restful風格的支持詳解
- Go語言Gin處理響應方式詳解
- java中TESTful架構原理分析
- golang進行簡單權限認證的實現