1. 路由组

在实际的项目开发中,均是模块化开发

同一模块内的功能接口,往往会有相同的接口前缀,这种可以用路由组来进行分类处理。

比如下面这几组接口:

注册:http://localhost:8080/user/register
登陆:http://localhost:8080/user/login
用户信息:http://localhost:8080/user/info

 

gin框架可以使用路由组来实现对路由的分类

路由组是router.Group中的一个方法,对于请求进行分组

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

	// 注册路由组
	routerGroup := engine.Group("/user")

	routerGroup.POST("/register", func(ctx *gin.Context) {})
	routerGroup.POST("/login", func(ctx *gin.Context) {})
	routerGroup.GET("/info", func(ctx *gin.Context) {})

	engine.Run()
}

 

2. 中间件

 在实际的业务开发中,一个完整的系统可能要包含鉴权认证、权限管理、安全检查、日志记录等多个维度的系统支持

鉴权认证、权限管理、安全检查等业务都是属于全系统的业务,和具体的业务没有直接关联

因此在开发中,为了更好的梳理系统架构,可以将以上这些业务单独抽离出来,以插件化的方式进行对接

这种通用业务独立开发并灵活配置使用的组件,称之为中间件,其位于服务器和实际业务处理程序之间

 

 

2.1 gin的中间件

在gin中,中间件的类型定义如下:

// HandlerFunc defines the handler used by gin middleware as return value
type HandlerFunc func(*Context)

HandlerFunc是一个函数类型,接收一个Context参数。用于编写程序处理函数并返回HandleFunc类型,作为中间件定义

 

2.2 中间件Use用法

关于初始化gin engine的gin.Default()方法的实现中也使用了两个中间件

// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
	debugPrintWARNINGDefault()
	engine := New()
	engine.Use(Logger(), Recovery())
	return engine
}

 

我们可以跳转到Logger()中间件的定义,去看看实现一个中间件的格式是什么

// Logger instances a Logger middleware that will write the logs to gin.DefaultWriter.
// By default, gin.DefaultWriter = os.Stdout.
func Logger() HandlerFunc {
	return LoggerWithConfig(LoggerConfig{})
}

 

2.3 自定义中间件

根据上文关于中间件的描述中,我们可以自定义一个特殊需求的中间件,中间件类型是函数,有两条标准:

  • func函数
  • 返回值类型为HandlerFunc

比如我们现在有一个需求,实现一个中间件,其功能就是打印出请求的path和method:

// RequestInfos 实现一个中间件
func RequestInfos() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		path := ctx.FullPath()
		method := ctx.Request.Method
		fmt.Println(path, method)
	}
}

  

使用或者注册中间件时有两种方式,一种是直接使用engine.Use(),那么所有接口都会经过这个中间件处理

// 使用中间件,所有接口都经过这个中间件
engine.Use(RequestInfos())

  

或者为某一个处理注册一个中间件,那么只有这一个请求会经过该中间件

engine.GET("/query", RequestInfos(), func(ctx *gin.Context) {
	ctx.JSON(http.StatusOK, map[string]interface{}{
		"code":    1,
		"message": ctx.FullPath(),
	})
})

 

2.4 context.Next函数

在上面自定义中间件RequestInfos()中,打印了请求的路径和请求的method,接着去执行了正常的业务处理函数

如果我们想输出业务处理的结果,就应该使用context.Next来实现

context.Next可以将中间件代码一分为二:

  • Next()之前的代码会在请求处理之前执行
  • 当中间件执行流遇到context.Next时,会中断执行,转而执行业务逻辑
  • 当业务逻辑执行完之后,再次回到Next函数处,继续向下执行中间件逻辑,从而获取业务执行之后的结果

 具体用法如下:

// RequestInfos 实现一个中间件
func RequestInfos() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		path := ctx.FullPath()
		method := ctx.Request.Method
		fmt.Println(path, method)

		ctx.Next() // 在此处一分为二

		fmt.Println(ctx.Writer.Status())
	}
}