更多文章 狂點 -> ISLAND前端
通過上一章節的介紹,搭建一個簡單的 Gin
web
項目很是容易,同時也引入了一些新的概念,好比說:路由 Router
。git
路由是一個很是重要的概念,全部的接口都要有路由來進行管理。github
Gin
的路由支持 GET
, POST
, PUT
, DELETE
, PATCH
, HEAD
, OPTIONS
請求,同時還有一個 Any
函數,能夠同時支持以上的全部請求。web
將上一章節的代碼添加其餘請求方式的路由,並編寫單元測試。數組
// 省略其餘代碼
// 添加 Get 請求路由
router.GET("/", func(context *gin.Context) {
context.String(http.StatusOK, "hello gin get method")
})
// 添加 Post 請求路由
router.POST("/", func(context *gin.Context) {
context.String(http.StatusOK, "hello gin post method")
})
// 添加 Put 請求路由
router.PUT("/", func(context *gin.Context) {
context.String(http.StatusOK, "hello gin put method")
})
// 添加 Delete 請求路由
router.DELETE("/", func(context *gin.Context) {
context.String(http.StatusOK, "hello gin delete method")
})
// 添加 Patch 請求路由
router.PATCH("/", func(context *gin.Context) {
context.String(http.StatusOK, "hello gin patch method")
})
// 添加 Head 請求路由
router.HEAD("/", func(context *gin.Context) {
context.String(http.StatusOK, "hello gin head method")
})
// 添加 Options 請求路由
router.OPTIONS("/", func(context *gin.Context) {
context.String(http.StatusOK, "hello gin options method")
})
// 省略其餘代碼
複製代碼
單元測試,只展現一個 Post
請求函數,基本與 Get
請求一致,其餘代碼詳見文末 Github 地址。瀏覽器
// router("/") post 測試
func TestIndexPostRouter(t *testing.T) {
router := initRouter.SetupRouter()
w := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodPost, "/", nil)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, "hello gin post method", w.Body.String())
}
複製代碼
此時運行單元測試,全部測試完美經過。函數
可是也有一個問題,全部的請求對應的路由內函數基本同樣,只是有細微的差異,可是咱們卻每一個路由裏都完完整整的寫了一遍,因此咱們要將公共邏輯抽取出來。post
func retHelloGinAndMethod(context *gin.Context) {
context.String(http.StatusOK, "hello gin "+strings.ToLower(context.Request.Method)+" method")
}
複製代碼
咱們將方法的公共部分抽取出來,並經過 context.Request.Method
將請求的方法提取出來 ,並將其轉化爲小寫。此時就能夠改造咱們的路由了,將原有的路由中的函數去掉換成咱們所編寫的新的函數。單元測試
// 添加 Get 請求路由
router.GET("/", retHelloGinAndMethod)
// 添加 Post 請求路由
router.POST("/", retHelloGinAndMethod)
// 添加 Put 請求路由
router.PUT("/", retHelloGinAndMethod)
// 添加 Delete 請求路由
router.DELETE("/", retHelloGinAndMethod)
// 添加 Patch 請求路由
router.PATCH("/", retHelloGinAndMethod)
// 添加 Head 請求路由
router.HEAD("/", retHelloGinAndMethod)
// 添加 Options 請求路由
router.OPTIONS("/", retHelloGinAndMethod)
複製代碼
此時運行單元測試,仍舊是完美經過。測試
通過上面簡單的例子的演示和操做,如今咱們大概能夠了解到路由須要傳入兩個參數,一個爲路徑,另外一個爲路由執行的方法,咱們叫作它處理器 Handler
,並且,該參數是可變長參數。也就是說,能夠傳入多個 handler,造成一條 handler chain 。
同時對 handler 該函數有着一些要求,該函數須要傳入一個 Gin.Context
指針,同時要經過該指針進行值得處理。
Handler 函數能夠對前端返回 字符串,Json,Html 等多種格式或形式文件,以後咱們會慢慢逐一介紹。
知道了路由支持的方法和對應的處理器,那麼接下來就應該瞭解如何從路由中獲取參數。
編寫一個新的路由,以下:
//省略其餘代碼
// 添加 user
router.GET("/user/:name",handler.Save)
// 省略其餘代碼
複製代碼
此時咱們發現,在原來只有 /
分隔符的狀況下出現了 /:
該符號就表示後面的字符串爲一個佔位符,用於將要進行的傳值。,此時咱們的路由爲 /user/{name}
咱們沒有必要把全部的 Handler 都寫到一個文件夾中,那樣會臃腫不堪,因此咱們新建一個文件夾handler
,在文件夾下創建 userHandler.go
文件,編寫該文件。
package handler
import (
"github.com/gin-gonic/gin"
"net/http"
)
func UserSave(context *gin.Context) {
username := context.Param("name")
context.String(http.StatusOK, "用戶已經保存")
}
複製代碼
一樣,咱們用 context.Param
能夠獲取路由路徑中的參數。
此時,就能夠編寫咱們的單元測試。
新創建一個 user_test.go
文件。
func TestUserSave(t *testing.T) {
username := "lisi"
router := initRouter.SetupRouter()
w := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodGet, "/user/"+username, nil)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, "用戶"+username+"已經保存", w.Body.String())
}
複製代碼
運行單元測試,測試經過。一樣咱們能夠運行咱們的項目在瀏覽器中輸入 localhost:8080/user/lisi
在瀏覽器頁面上也能夠看到 用戶lisi已經保存
固然,獲取參數的方法不止這一個。針對不一樣的路由,Gin 給出了不一樣的獲取參數的方法,好比形如:/user?name=lisi&age=18
。
咱們再次添加一個 Handler
,作爲處理。在 userHandler
中添加下面的方法。
// 經過 query 方法進行獲取參數
func UserSaveByQuery(context *gin.Context) {
username := context.Query("name")
age := context.Query("age")
context.String(http.StatusOK, "用戶:"+username+",年齡:"+age+"已經保存")
}
複製代碼
同時對路由進行添加和完善。
router.GET("/user", handler.UserSaveByQuery)
複製代碼
完成路由以後,就能夠從新編寫單元測試,完善項目。
func TestUserSaveQuery(t *testing.T) {
username := "lisi"
age := 18
router := initRouter.SetupRouter()
w := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodGet, "/user?name="+username+"&age="+strconv.Itoa(age), nil)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, "用戶:"+username+",年齡:"+strconv.Itoa(age)+"已經保存", w.Body.String())
}
複製代碼
運行測試,測試經過。而且能夠經過 瀏覽器訪問localhost:8080/user?name=lisi
,頁面上打印出用戶:lisi,年齡:18已經保存
。
固然,還能夠經過 context.DefaultQuery
方法,在獲取時,若是沒有該值則賦給一個默認值。
從新修改獲取年齡的代碼,將其改成如下代碼
age := context.DefaultQuery("age", "20")
複製代碼
從新編寫咱們的單元測試,並運行。
func TestUserSaveWithNotAge(t *testing.T) {
username := "lisi"
router := initRouter.SetupRouter()
w := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodGet, "/user?name="+username, nil)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, "用戶:"+username+",年齡:20已經保存", w.Body.String())
}
複製代碼
一樣也能夠經過瀏覽器訪問 /user?name=lisi
能夠看到瀏覽器上顯示 用戶:lisi,年齡:20已經保存
。
固然,還提供了其餘參數獲取方法, QueryArray
獲取數組和 QueryMap
獲取 map。
此時咱們再次看 SetupRouter
方法時,裏面的路由基本能夠分爲兩大類 /
和 /user
,若是往後在進行功能的添加,那麼勢必會出現大量的路由,因此咱們須要對路由進行一下管理,Gin 給咱們提供了路由分組。
先把 /
的路由寫到一塊兒,運行 index_test.go
單元測試。
index := router.Group("/")
{
// 添加 Get 請求路由
index.GET("", retHelloGinAndMethod)
// 添加 Post 請求路由
index.POST("", retHelloGinAndMethod)
// 添加 Put 請求路由
index.PUT("", retHelloGinAndMethod)
// 添加 Delete 請求路由
index.DELETE("", retHelloGinAndMethod)
// 添加 Patch 請求路由
index.PATCH("", retHelloGinAndMethod)
// 添加 Head 請求路由
index.HEAD("", retHelloGinAndMethod)
// 添加 Options 請求路由
index.OPTIONS("", retHelloGinAndMethod)
}
複製代碼
經過 router.Group
返回一個新的分組路由,經過新的分組路由把以前的路由進行簡單的修改。固然分組裏面仍舊能夠嵌套分組。
以前在請求方法中說到有一個 Any
函數能夠經過任何請求,此時咱們就能夠把 index
裏面全部的請求替換爲 Any
index := router.Group("/")
{
index.Any("", retHelloGinAndMethod)
}
複製代碼
運行單元測試,測試均可以經過。
此時也發現了單元測試的好處,雖然說以前花費了時間和經歷編寫了單元測試,可是往後的功能上修改,只須要進行運行單元測試就能夠知道咱們的功能是否正確,在後期的功能測試上大大減小了經歷和時間。
一樣咱們把 user
也進行分組,分組不只僅是將相同邏輯的代碼放到一塊兒,並且能夠提供相同的路由前綴,修改後的路由仍舊和以前一致。運行單元測試 user_test.go
,單元測試能夠徹底經過,證實咱們的代碼沒有問題。
userRouter := router.Group("/user")
{
userRouter.GET("/:name", handler.UserSave)
userRouter.GET("", handler.UserSaveByQuery)
}
複製代碼
經過簡單的路由的使用,基本明白了路由在 Gin 中的地位,也對一些常見的使用方式有了一些直觀的認識。