banner
biuaxia

biuaxia

"万物皆有裂痕,那是光进来的地方。"
github
bilibili
tg_channel

【轉載】Web框架Gin之Gin路由

title: 【轉載】Web 框架 Gin 之 Gin 路由
date: 2021-08-16 09:30:01
comment: false
toc: true
category:

  • Golang
  • Gin
    tags:
  • 轉載
  • Go
  • Golang
  • Gin
  • Web
  • 框架
  • 路由

本文轉載自:Web 框架 Gin | Gin 路由


Web 框架 Gin | Gin 路由#

Gin 是一個標準的 Web 服務框架,遵循 Restful API 接口規範,其路由庫是基於 httproute 實現的。

本節將從 Gin 路由開始,詳細講述各種路由場景下,如何通過 Gin 來實現。

基本路由#

Gin 支持 GET、POST、PUT、PATCH、DELETE、OPTIONS 等請求類型。

示例

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	route := gin.Default()

	// 設置一個get請求,其URL為/hello,並實現簡單的響應
	route.GET("/get", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "this is a get method response!",
		})
	})

	// 具體實現可單獨定義一個函數
	route.POST("/post", postHandler)

	route.PUT("/put", func(c *gin.Context) {

	})

	route.PATCH("/patch", func(c *gin.Context) {

	})

	route.DELETE("/delete", func(c *gin.Context) {

	})

	// ……

	route.Run()
}

func postHandler(c *gin.Context) {
	c.JSON(http.StatusOK, "this is a post method response!")
}

參數處理#

path 參數#

可通過 *gin.Context 的 Param 函數獲取請求 Path 中的參數,Path 路徑中參數以:開頭,如:/user/:name,能夠匹配路徑 /user/xx

示例

package main

import (
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
)

type User struct {
	Username string   `json:"username"`
	Sex      string   `json:"sex"`
	Age      int      `json:"age"`
	Labels   []string `json:"lalels"`
}

func main() {
	route := gin.Default()

	users := []User{
		{
			Username: "xcbeyond",
			Sex:      "男",
			Age:      18,
			Labels:   []string{"年輕", "帥"},
		},
	}

	// 請求path中存在參數
	route.GET("/user/:name", func(c *gin.Context) {
		// 獲取請求path中的參數
		name := c.Param("name")
		for _, user := range users {
			if user.Username == name {
				c.JSON(http.StatusOK, user)
				return
			}
		}
		c.JSON(http.StatusOK, fmt.Errorf("not found user [%s]", name))
	})

	route.Run()
}

查詢參數#

可通過 *gin.Context 的 Query 函數獲取參數,如:/user?name=xcbeyond

示例

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

type User struct {
	Username string   `json:"username"`
	Sex      string   `json:"sex"`
	Age      int      `json:"age"`
	Labels   []string `json:"lalels"`
}

func main() {
	route := gin.Default()

	users := []User{
		{
			Username: "xcbeyond",
			Sex:      "男",
			Age:      18,
			Labels:   []string{"年輕", "帥"},
		},
	}

	// 查詢參數,如:/user?name=xcbeyond
	route.GET("/user", func(c *gin.Context) {
		// 獲取中的參數
		name := c.Query("name")
		for _, user := range users {
			if user.Username == name {
				c.JSON(http.StatusOK, user)
				return
			}
		}
		c.JSON(http.StatusOK, "not found user "+name)
	})

	route.Run()
}

……#

路由分組#

Gin 提供了路由分組的能力,方便管理分組管理路由,將具有相同路由 URL 前綴的進行分類處理,常見於不同版本的分組,如:/api/v1/api/v1

此外,還支持多層分組。

示例

package main

import (
	"math/rand"
	"net/http"

	"github.com/gin-gonic/gin"
)

type User struct {
	Username string   `json:"username"`
	Sex      string   `json:"sex"`
	Age      int      `json:"age"`
	Labels   []string `json:"lalels"`
}

func main() {
	route := gin.Default()

	users := []User{
		{
			Username: "xcbeyond",
			Sex:      "男",
			Age:      18,
			Labels:   []string{"年輕", "帥"},
		},
		{
			Username: "niki",
			Sex:      "女",
			Age:      16,
			Labels:   []string{"漂亮"},
		},
	}

	// api分組
	api := route.Group("/api")
	{
		// v1分組
		v1 := api.Group("/v1")
		{
			v1.GET("/user/:name", func(c *gin.Context) {
				name := c.Param("name")
				for _, user := range users {
					if user.Username == name {
						c.JSON(http.StatusOK, user)
						return
					}
				}
				c.JSON(http.StatusOK, "not found user :"+name)
			})
		}

		// v2分組
		v2 := api.Group("/v2")
		{
			v2.GET("/user/:name", func(c *gin.Context) {
				name := c.Param("name")
				for _, user := range users {
					if user.Username == name {
						c.JSON(http.StatusOK, user)
						return
					}
				}

				// 如果沒有查到,則隨機返回一個
				user := users[rand.Intn(len(users)-1)]
				c.JSON(http.StatusOK, "not found user ["+name+"],but user ["+user.Username+"] is exist!")
			})
		}
	}

	route.Run()
}

路由拆分#

上述路由示例中,都是將所有路由信息寫在同一個源文件、函數中,但在實際項目中會涉及大量的接口,寫在一起就顯得太不合適了。

在實際項目中,我們更傾向於將路由代碼拆分出來,可拆分為單獨的包、多個路由源文件等。

根據實際項目的規模大小,可分不同粒度拆分。

(以下路由拆分僅供參考,可根據具體項目靈活調整!)

路由拆分為單獨源文件或包#

將路由實現分離到單獨的包下,使得項目結構更加清晰。項目結構如下:

.
├── main.go
└── routes
    └── routes.go

示例完整源碼:route-split-v1

/routes/routes.go 文件中實現並註冊路由信息:

package routes

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

type User struct {
	Username string   `json:"username"`
	Sex      string   `json:"sex"`
	Age      int      `json:"age"`
	Labels   []string `json:"lalels"`
}

var users []User

// 為方便測試,則直接通過init方式賦值。實際項目中,一般通過數據庫等其它方式查詢獲取數據。
func init() {
	users = []User{
		{
			Username: "xcbeyond",
			Sex:      "男",
			Age:      18,
			Labels:   []string{"年輕", "帥"},
		},
		{
			Username: "niki",
			Sex:      "女",
			Age:      16,
			Labels:   []string{"漂亮"},
		},
	}
}

// SetupRouter 配置路由
func SetupRouter() *gin.Engine {
	route := gin.Default()

	route.GET("/user/:name", querUserHandler)
	// 其它更多路由

	return route
}

// handler
func querUserHandler(c *gin.Context) {
	name := c.Param("name")
	for _, user := range users {
		if user.Username == name {
			c.JSON(http.StatusOK, user)
			return
		}
	}
	c.JSON(http.StatusOK, "not found user :"+name)
}

並在 main.go 中調用路由配置函數 SetupRouter 即可:

func main() {
	route := routes.SetupRouter()
	if err := route.Run(); err != nil {
		fmt.Printf("startup server failed,err: %v", err)
	}
}

路由拆分為多個源文件#

當隨著項目的業務功能豐富,體量變大,所有的路由都寫在一個 routes.go 源文件中,將會導致這個源文件越來越臃腫,不便於後期維護和閱讀。

因此,可根據某種維度拆分為多個路由文件來實現不同業務功能。項目結構如下:

.
├── main.go
└── routes
    ├── auth.go
    ├── routes.go
    └── user.go

示例完整源碼:route-split-v2

在 routes 包下,根據某種維度拆分為多個路由實現文件,如:根據業務模塊,可拆分為認證模塊(auth.go)、用戶模塊(user.go)等,並在各自路由文件中實現具體的業務功能,並進行路由註冊。

如,認證模塊 auth.go:

package routes

import (
	"github.com/gin-gonic/gin"
)

// AuthRegister route register
func AuthRegister(e *gin.Engine) {
	e.GET("/auth/login", loginHandler)
	e.POST("/auth/logout", logoutUserHanler)
	// ……
}

// loginHandler
func loginHandler(c *gin.Context) {

}

// logoutUserHanler
func logoutUserHanler(c *gin.Context) {

}

其中,定義 AuthRegister 函數將該模塊下所有路由進行註冊,注意該函數為大寫字母開頭,作為全局函數能夠被包外 main.go 調用。

routes/routes.go 中,配置路由,並統一註冊所有模塊的路由:

package routes

import (
	"github.com/gin-gonic/gin"
)

// SetupRouter 配置路由
func SetupRouter() *gin.Engine {
	route := gin.Default()

	// other config

	// register all route.
	UserRegister(route)
	AuthRegister(route)
	// ……

	return route
}

main.go 和上一版本一樣,作為程序等入口:

func main() {
	route := routes.SetupRouter()
	if err := route.Run(); err != nil {
		fmt.Printf("startup server failed,err: %v", err)
	}
}

參考資料:

  1. api-examples
  2. gin 框架路由拆分與註冊
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。