gin路由分组和中间件

路由分组

路由分组可以使路由结构更加清晰,更加方便管理路由。
官方演示代码

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

	// Simple group: v1
	v1 := router.Group("/v1")
	{
		v1.POST("/login", loginEndpoint)
		v1.POST("/submit", submitEndpoint)
		v1.POST("/read", readEndpoint)
	}

	// Simple group: v2
	v2 := router.Group("/v2")
	{
		v2.POST("/login", loginEndpoint)
		v2.POST("/submit", submitEndpoint)
		v2.POST("/read", readEndpoint)
	}

	router.Run(":8080")
}

即在开启路由后,调用router.Group()方法进行分组,方法参数为相对路径。
返回的参数为RouterGroup结构体,RouterGroup在内部用于配置路由器,一个RouterGroup与一个前缀和一个处理程序数组(中间件)相关。

中间件

中间件时在请求到达路由的方法的前和后进行的一系列操作

使用中间件

官方演示代码

func main() {
	// 创建一个不包含中间件的路由器
	r := gin.New()

	// 全局中间件
	// 使用 Logger 中间件
	r.Use(gin.Logger())

	// 使用 Recovery 中间件
	r.Use(gin.Recovery())

	// 路由添加中间件,可以添加任意多个
	r.GET("/benchmark", MyBenchLogger(), benchEndpoint)

	// 路由组中添加中间件
	// authorized := r.Group("/", AuthRequired())
	// exactly the same as:
	authorized := r.Group("/")
	// per group middleware! in this case we use the custom created
	// AuthRequired() middleware just in the "authorized" group.
	authorized.Use(AuthRequired())
	{
		authorized.POST("/login", loginEndpoint)
		authorized.POST("/submit", submitEndpoint)
		authorized.POST("/read", readEndpoint)

		// nested group
		testing := authorized.Group("testing")
		testing.GET("/analytics", analyticsEndpoint)
	}

	// Listen and serve on 0.0.0.0:8080
	r.Run(":8080")
}

在路由器或组上调用use操作,后面传入中间件函数即可使用中间件。

创建中间件

首先分析Use方法

func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
	engine.RouterGroup.Use(middleware...)
	engine.rebuild404Handlers()
	engine.rebuild405Handlers()
	return engine
}

use接收一个HandlerFunc结构的中间件参数。
HandlerFunc结构为

type HandlerFunc func(*Context)

所以创建中间件,即需要自己构造一个,返回值为*Context的方法,其中添加自己的具体操作。
例如:

package main

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

func middle() gin.HandlerFunc {
	return func(c *gin.Context) {
		fmt.Println("方法前")
		c.Next()
		fmt.Println("方法后")
	}
}
func main() {
	r := gin.Default()
	r.Use(middle())
	r.GET("/testmiddle", func(c *gin.Context) {
		fmt.Println("get方法")
		c.JSON(200, gin.H{
			"meg": "test",
		})
	})
	r.Run()
}

在这里插入图片描述
多中间件的运行流程测试:

package main

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

func middle1() gin.HandlerFunc {
	return func(c *gin.Context) {
		fmt.Println("middle1方法前")
		c.Next()
		fmt.Println("middle1方法后")
	}
}
func middle2() gin.HandlerFunc {
	return func(c *gin.Context) {
		fmt.Println("middle2方法前")
		c.Next()
		fmt.Println("middle2方法后")
	}
}
func main() {
	r := gin.Default()
	r.Use(middle1(), middle2())
	r.GET("/testmiddle", func(c *gin.Context) {
		fmt.Println("get方法")
		c.JSON(200, gin.H{
			"meg": "test",
		})
	})
	r.Run()
}

在这里插入图片描述
发现中间件调用顺序是洋葱模式

简单中间件应用实验

简单做一个中间件身份拦截器实验
目的是在未经行身份验证的情况下防止用户直接通过路径访问主页面。
由于还未进行cookie,session,token等功能的实验,暂且将用户信息保存到本地
登录功能实现

r := gin.Default()
	r.LoadHTMLGlob("./webapp/*")
	v1 := r.Group("/login")
	v2 := r.Group("/desktop").Use(middle())
	v1.GET("/loginN", func(c *gin.Context) {
		c.HTML(200, "login.html", nil)
	})
	v1.POST("/loginN", func(c *gin.Context) {
		username = c.PostForm("hsid")
		password = c.PostForm("hspwd")
		fmt.Println(username)
		fmt.Println(password)
		if username == "123" && password == "456" {
			c.Redirect(302, "/desktop/1")
		}
	})

desktop的中间件代码

func middle() gin.HandlerFunc {
	return func(c *gin.Context) {
		if username == "123" && password == "456" {
			c.Next()
		} else {
			c.Redirect(302, "/login/loginN")
		}
	}
}

实验全部代码

package main

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

var username string
var password string

func middle() gin.HandlerFunc {
	return func(c *gin.Context) {
		if username == "123" && password == "456" {
			c.Next()
		} else {
			c.Redirect(302, "/login/loginN")
		}
	}
}
func main() {
	r := gin.Default()
	r.LoadHTMLGlob("./webapp/*")
	v1 := r.Group("/login")
	v2 := r.Group("/desktop").Use(middle())
	v1.GET("/loginN", func(c *gin.Context) {
		c.HTML(200, "login.html", nil)
	})
	v1.POST("/loginN", func(c *gin.Context) {
		username = c.PostForm("hsid")
		password = c.PostForm("hspwd")
		fmt.Println(username)
		fmt.Println(password)
		if username == "123" && password == "456" {
			c.Redirect(302, "/desktop/1")
		}
	})
	v2.GET("/1", func(c *gin.Context) {
		c.HTML(200, "desktop.html", nil)
	})
	r.Run()
}

测试结果
当没有在登录页面经行登录验证,之间通过地址进入系统主页面,预想会之间跳转至登陆页面。
在这里插入图片描述
输入主页面地址,被跳转至登陆页面
在这里插入图片描述
成功

当在登录页面完成登录验证,再直接通过地址进入主页,预想会直接跳到主页。
以传入正确账号密码
在这里插入图片描述
输入主页面地址
在这里插入图片描述
成功进入主页面
在这里插入图片描述
成功

当此时在登录页面,更新后台存储的账号密码,改为错误账号密码,预想无法直接通过地址进入主页面,而是会跳转至登录页面
修改密码,为错误密码
在这里插入图片描述
输入主页面地址
在这里插入图片描述
跳转至登陆页面
在这里插入图片描述
成功

简单实现了中间件作为身份拦截器功能。实验发现,重定向端口与原端口号重合时,会报错。小问题,但是编写大量页面时还是提前拟定端口号,避免错误。