Gin(二):路由使用

更多文章 狂點 -> ISLAND前端

通過上一章節的介紹,搭建一個簡單的 Gin web 項目很是容易,同時也引入了一些新的概念,好比說:路由 Routergit

路由是一個很是重要的概念,全部的接口都要有路由來進行管理。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,造成一條 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 中的地位,也對一些常見的使用方式有了一些直觀的認識。

本章節代碼

Github

相關文章
相關標籤/搜索