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)
}
}
參考資料: