路由控制
引入
我们前面写过的代码,路由比较简单,但是实际开发中往往很复杂,我们可能要这样写
r.GET("/user/login", login)
r.POST("/user/register", register)
r.GET("/user/info", info)
r.GET("/video/list", videoList)
r.POST("/video/upload", videoUpload)
r.GET("/shop/list", shopList)
// ... 还有 500 个接口
这样同样的路径,我们要写n遍,同时不符合模块化开发的思想,所有东西都是散的。
Gin提供了Group将路由按照功能或者版本进行打包管理。
核心函数解析
1.v1 := r.Group("/path")
创建一个路由组,返回一个RouterGroup指针,同时v1对象拥有和r几乎一样的方法,再v1下注册的所有路由都会带上/path的前缀
代码示例
我们要把接口分为v1和v2版本,同时内部区分用户模块
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
v1 := r.Group("/v1")
{
user := v1.Group("/user")
{
user.GET("/info", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"msg": "v1 版本的用户信息"})
})
user.POST("/login", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"msg": "v1的登录"})
})
}
v1.GET("/news", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"msg": "v1 新闻"})
})
}
v2 := r.Group("/v2")
{
user := v2.Group("/user")
{
user.GET("/info", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"msg": "我是 v2 版本的用户信息"})
})
}
}
r.Run(":8080")
}
设计哲学
1.{.....}并不是Go语法的强制要求,但是写上之后可以更加清晰地展示路由的层级,同时为了方便后面加中间件。
2.已经说过的树状结构,因为Gin的路由底层是一棵树,group就相当于分支,请求就相当于是叶子,也完美契合了RESTFUL API的层级结构,定位功能代码也更加的方便。
中间件
引入
我们的web程序通常要有管理端和用户端,如果我们不做处理,那么我可以直接通过管理端的路由,直接访问管理端的数据,那么我们要在所有接口的开头校验Token吗,那太麻烦了,Gin使用中间件来解决这个问题。
想象你的 Web 服务器是一个洋葱,核心业务逻辑(比如“查询用户数据”)在洋葱的最中心。 中间件就是洋葱的一层层表皮。 当请求(Request)进来时,必须先穿过第一层皮(日志),再穿过第二层皮(鉴权),最后才能到达中心。 当响应(Response)出去时,又要反向穿过这些皮。
核心函数解析
c.Next()—— 放行
- 像接力棒一样。当中间件执行到这行代码时,它会暂停,先把控制权移交给下一个中间件或最终的业务逻辑。
- 等后面的人执行完了,
Next()下面的代码还会继续执行!这非常适合做耗时统计(请求前记个时间,请求后算个差值)。
2.c.Abort() —— 拦截
- 鉴权失败。如果发现用户没有 Token,直接调用
c.Abort(),后续的所有业务逻辑都不会执行,直接返回错误给前端。
3.中间件本质上是gin.HandlerFunc
type HandlerFunc func(*Context)- 统一了中间件和业务逻辑的接口标准,让 Gin 的底层实现变得非常简洁。
代码示例
package main
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
fmt.Println("中间件启动")
token := c.Query("token")
if token != "secret_password" {
fmt.Println("failed 拦截请求")
c.JSON(http.StatusUnauthorized, gin.H{"err": "身份验证失败"})
c.Abort()
return
}
fmt.Println("鉴权通过")
c.Next()
fmt.Println("请求处理完毕")
}
}
func main() {
r := gin.Default()
r.GET("/public", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"msg": "所有人可见"})
})
adminGroup := r.Group("/admin")
adminGroup.Use(AuthMiddleware())
/*挂载中间件*/
{
adminGroup.GET("/dashboard", func(c *gin.Context) {
fmt.Println("--- 核心业务逻辑执行中 ---")
c.JSON(http.StatusOK, gin.H{"msg": "欢迎进入管理员后台"})
})
}
r.Run(":8080")
}
设计哲学
1.AOP(面向切面编程)
有些功能是所有业务都需要的,如果把这些代码,混在业务逻辑,会非常的混乱,Gin把这些功能像切面一样插入到流程中,业务逻辑只管业务,中间件只管安保,实现了完美的解耦
2.责任链模式
Gin内部维护了一个HandlersChain,当你调用Use时,就是往这个数组里面append函数,请求进来的时候就做一个for循环,依次执行函数,也就解释了,为什么有Next(),实际上就是去调用数组里面的下一个元素
补充案例(耗时统计中间件)
func TimerMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
t := time.Now()
c.Next()
latency := time.Since(t)
path := c.Request.URL.Path
if latency > 200*time.Millisecond {
fmt.Printf("[慢请求警告] Path: %s | 耗时: %v\n", path, latency)
} else {
fmt.Printf("[正常] Path: %s | 耗时: %v\n", path, latency)
}
}
}










