Day05:Gin框架快速入门05 中间件和路由 | 青训营

150 阅读5分钟

导读

本套笔记是为了学习过其他语言框架,想要快速掌握gin框架推行的一套笔记。 虽然为了青训营而制作的一套笔记,但其他想要学go的程序员也可以通过这个上手go世界,后续会带你快速上手gorm,学完这两个之后,简体版抖音基本上就可以独立完成了,后续还会进行大项目的讲解开发,制作不易,喜欢的就点个关注吧。

注意

代码详解大部分是注释的形式给出,请留意代码注释。

Gin框架介绍

导读:Gin是一个非常受欢迎的Golang Web框架,它旨在提供高性能、易用和轻量级的解决方案。

中间件

中间件(Middleware)是在应用程序处理请求和生成响应之间执行的一系列功能组件。它请求到达目标处理程序之前或响应回传给客户端之前,对请求和响应进行预处理或后处理。类似java的拦截器。

大白话:Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数,这个钩子函数就叫中间件。

在 Gin 框架中,gin.HandlerFunc 是一个具有特定签名的函数类型,它接受一个 *gin.Context 参数,并没有返回值。这个函数类型被用作中间件函数和路由处理函数的类型,Gin中的中间件必须是一个gin.HandlerFunc类型。

单独注册中间件

package main  
  
import (  
"fmt"  
"github.com/gin-gonic/gin"  
"net/http"  
)  
  
  
func get(c *gin.Context) {  
fmt.Println("get运行了")  
c.JSON(http.StatusOK, gin.H{  
"msg": "ok",  
})  
}  
  
//定义一个中间件  
func m1(c *gin.Context) {  
fmt.Println("中间件运行了")  
}  
  
func main() {  
router := gin.Default()  
  
router.GET("/get", m1, get)  
  
router.Run(":8080")  
}

当我们运行之后,发送send,会执行m1,之后执行get。

image.png postman接收到信息

image.png

多个中间件

router.GET,后面可以跟很多HandlerFunc方法,这些方法其实都可以叫中间件

package main  
  
import (  
"fmt"  
"github.com/gin-gonic/gin"  
"net/http"  
)  
  
func get(c *gin.Context) {  
fmt.Println("get运行了")  
c.JSON(http.StatusOK, gin.H{  
"msg": "ok",  
})  
}  
  
// 定义一个中间件  
func m1(c *gin.Context) {  
fmt.Println("m1运行了")  
}  
  
func m2(c *gin.Context) {  
fmt.Println("m2运行了")  
}  
  
func main() {  
router := gin.Default()  
  
router.GET("/get", m1, get,m2)  
  
router.Run(":8080")  
}

运行结果如下

image.png 先运行m1,之后运行get,最后运行m2,以此类推,也可以增加更多的中间件

中间件拦截响应

只需要修改m1

// 定义一个中间件  
func m1(c *gin.Context) {  
fmt.Println("m1运行了")

//用于终止请求的处理流程并立即返回响应
c.Abort()  
}

运行结果

image.png 可以看到只有m1运行了

中间件放行

使用c.next()进行放行,会直接运行下一个方法,下一个方法运行结束之后运行c.next()之后的方法

修改代码

func get(c *gin.Context) {  
fmt.Println("get运行了")  
c.JSON(http.StatusOK, gin.H{  
"msg": "ok",  
})  
c.Next()  
fmt.Println("get再次运行")  
}  
  
// 定义一个中间件  
func m1(c *gin.Context) {  
fmt.Println("m1运行了")  
c.Next()  
fmt.Println("m1再次运行")  
}  
  
func m2(c *gin.Context) {  
fmt.Println("m2运行了")  
c.Next()  
fmt.Println("m2再次运行")  
}

运行结果如图

image.png

注意:如果其中一个中间件响应了c.Abort(),后续中间件将不再执行,直接按照顺序走完所有的响应中间件

全局注册中间件

在 Gin 框架中,Use 方法用于将中间件函数注册到全局中间件链中。全局中间件会在每个请求处理之前都被执行,无论请求的路径是什么。使用Use去注册全局中间件,Use接收的参数也是多个HandlerFunc

package main  
  
import (  
"fmt"  
"github.com/gin-gonic/gin"  
"net/http"  
)  
  
func get(c *gin.Context) {  
fmt.Println("get运行了")  
c.JSON(http.StatusOK, gin.H{  
"msg": "ok",  
})  
c.Next()  
fmt.Println("get再次运行")  
}  
  
func m10(c *gin.Context) {  
fmt.Println("m10运行了")  
}  
  
func main() {  
router := gin.Default()  
  
router.Use(m10)  
router.GET("/get", get)  
router.POST("/post", get)  
  
router.Run(":8080")  
}

我们使用getpost分别发送请求,m10都会先运行。

中间件传递数据

我们使用c.Get()c.Set()方法来传递数据

c.Set() 是 Gin 框架中的方法,用于在请求处理过程中设置键值对数据。

c.Get() 是 Gin 框架中的方法,用于获取在请求处理过程中设置的键值对数据。

package main  
  
import (  
"fmt"  
"github.com/gin-gonic/gin"  
"net/http"  
)  
  
func get(c *gin.Context) {  
name, _ := c.Get("name")//获得数据  
fmt.Println(name)  
c.JSON(http.StatusOK, gin.H{  
"msg": "ok",  
})  
c.Next()  
  
}  
  
func m10(c *gin.Context) {  
fmt.Println("m10运行了")  
c.Set("name", "xiaoming") //定义一个数据 
}  
  
func main() {  
router := gin.Default()  
  
router.Use(m10)  
router.GET("/get", get)  
  
router.Run(":8080")  
}

运行结果

image.png 可以看到成功拿到了数据name

Set()第二个参数是any类型,所有我们可以用它传任意类型,在接收的时候做好断言即可 例如一个结构体

package main  
  
import (  
"fmt"  
"github.com/gin-gonic/gin"  
"net/http"  
)  
  
type User struct {  
Name string  
Age int  
} //定义user结构体 
  
func get(c *gin.Context) {  
name, _ := c.Get("user") //获得结构退 
fmt.Println(name)//打印结构体  

c.JSON(http.StatusOK, gin.H{  
"msg": "ok",  
})//响应信息 
c.Next()  
  
}  
  
func m10(c *gin.Context) {  
fmt.Println("m10运行了")  
c.Set("user", User{  
Name: "xiaoming",  
Age: 18,  
}) 构造结构体user 
}  
  
func main() {  
router := gin.Default()  
  
router.Use(m10)  
router.GET("/get", get)  
  
router.Run(":8080")  
}

运行结果

image.png

路由

路由分组

将一系列的路由放到一个组下,统一管理

package main  
  
import (  
"github.com/gin-gonic/gin"  
"net/http"  
)  
  
func get(c *gin.Context) {  
c.JSON(http.StatusOK, gin.H{  
"msg": "ok",  
})  
}  
  
func main() {  
router := gin.Default()  
  
r := router.Group("/group") //分组 

r.GET("/get", get)  
r.POST("/post", get)  
  
router.Run(":8080")  
}

这样在postman里面我们如果要发送请求,只需要这样

image.png

image.png 两个响应的结果都是一样的

image.png

路由分组注册中间件

就是在分组后面加上Use就可以了

package main  
  
import (  
"fmt"  
"github.com/gin-gonic/gin"  
"net/http"  
)  
  
func middle(c *gin.Context) {  
fmt.Println("middle ...in")  
}  
  
func get(c *gin.Context) {  
c.JSON(http.StatusOK, gin.H{  
"msg": "ok",  
})  
}  
  
func main() {  
router := gin.Default()  
  
r := router.Group("/group").Use(middle) //分组并且使用全局中间件 
r.GET("/get", get)  
r.POST("/post", get)  
  
router.Run(":8080")  
}

分别发送请求之后

image.png

权限验证

以前后端最流行的jwt为例,如果用户登录了,前端发来的每一次请求都会在请求头上携带上token

后台拿到这个token进行校验,验证是否过期,是否非法

如果通过就说明这个用户是登录过的

不通过就说明用户没有登录

package main  
  
import (  
"github.com/gin-gonic/gin"  
"net/http"  
)  
  
 //验证jwt令牌是否正确的方法
 //每次都要验证
func JwtTokenMiddleware(c *gin.Context) {  
// 获取请求头的token  
token := c.GetHeader("token")  
// 调用jwt的验证函数  
if token == "1234" {  
// 验证通过  
c.Next()  
return  
}  
// 验证不通过  
c.JSON(200, gin.H{"msg": "权限验证失败"})  
c.Abort()  
}  
  

func get(c *gin.Context) {  
c.JSON(http.StatusOK, gin.H{  
"msg": "ok",  
})  
}  
  
func main() {  
router := gin.Default()  
  
r := router.Group("/group").Use(JwtTokenMiddleware)  
r.GET("/get", get)  
r.POST("/post", get)  
  
router.Run(":8080")  
}

我们使用postman发送请求

1: 如果token != 1234

image.png

2: 如果token = 1234

image.png