Goweb——Gin 入门

路由控制

引入

我们前面写过的代码,路由比较简单,但是实际开发中往往很复杂,我们可能要这样写

 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)出去时,又要反向穿过这些皮。

核心函数解析

  1. 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)
  }
  }
 }
暂无评论

发送评论 编辑评论


				
上一篇
下一篇