golang gin后端开发框架(三):路由组和中间件
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函数处,继续向下执行中间件逻辑,从而获取业务执行之后的结果
具体用法如下:
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())
}
}