一、简述
gin中间件类似java的拦截器,通过中间件可以在处理具体的route请求时,提前做一些业务。比如用户有效性的校验,特定日志格式的打印,错误日志的打印等等。
一个个中间件组成一条中间件链,对HTTPRequest请求进行拦截处理,实现了代码的解耦和分离,并且中间件之间相互不用感知到,每个中间件只需要处理自己需要处理的事情即可。
gin的中间件大致可以分为两类,全局中间件和路由中间件,下面记录gin的中间件学习过程中的一些问题。
二、全局中间件
1、默认中间件
使用router := gin.Default()
定义route时,默认带了Logger()
和Recovery()
。我们可以使用BasicAuth()
中间件做一些简单的用户权限的认证
// 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 } |
gin的中间件是通过Use方法设置的,它接受一个可变的参数,所以我们可以设置多个中间件
// Use adds middleware to the group, see example code in GitHub. func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes { group.Handlers = append(group.Handlers, middleware...) return group.returnObj() } |
一个gin的中间件,其实就是一个HandleFunc
router.GET( "/" , func(c *gin.Context) { c.JSON( 200 , "首页" ) }) 后边的func(c *gin.Context) {}就是一个HandleFunc |
2、自定义中间件
func main() { // 初始化环境 initCtx, cancel := context.WithCancel(context.Background()) defer cancel() //加载服务配置 bjh.InitServicer(initCtx) // 默认中间件引擎 router := gin.Default() router.Use(bjh.LoginAuth()) router.GET( "/" , func(c *gin.Context) { }) _ = router.Run( ":8000" ) } go get gopkg.in/cas.v1 func LoginAuth() gin.HandlerFunc { return func(ctx *gin.Context) { transCfg := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true }, // 不验证证书 } client := &http.Client{Transport: transCfg} u, _ := url.Parse(uuap.AuthInterface) newClient := cas.NewClient(&cas.Options{URL: u, Client: client}) h := newClient.HandleFunc(func(w http.ResponseWriter, r *http.Request) { ticket := r.FormValue( "ticket" ) devtools.Log( "ticket--------1" , ticket) if ticket == "" || !cas.IsAuthenticated(r) { cas.RedirectToLogin(w, r) devtools.Log( "redirect" , "go out" ) ctx.Abort() return } _, _ = fmt.Fprintf(w, "Hello %s\n" , cas.Username(r)) devtools.Log( "ticket--------2" , ticket) }) h.ServeHTTP(ctx.Writer, ctx.Request) ctx.Next() } } |
示例:
获取请求中的sessionid,写入到context中,context.Next()
跳到下一个中间件中,下一个中间件会对这个session进行有效性校验(sessionid是否存在,是否过期等)
//用户身份认证中间件 func Validate() gin.HandlerFunc { return func(context *gin.Context) { // 参数的或者以及部分业务逻辑 var sessionid = "" cookie, err := context.Request.Cookie(defs.Cookie_key) if err == nil && cookie != nil { sessionid = cookie.Value } else { sessionid = session.GenerateSess(login.UserName) } context.Set( "sessionid" , sessionid) context.Next() return } } |
3、路由中间件
只是针对具体的路由规则
示例:
下面定义了一个分组路由/user
,分组路由下的/login
需要验证用户名和密码,这个路由使用的是路由中间件Validate()
,通过查库验证用户名和密码。而其余的路由/manager/show
和/manager/modify
使用了路由中间件handles.Cookie()
,这个中间件验证用户在登陆成功后的sessionid
的有效性
// 设置user路由组 { userGroup := router.Group( "/user" ) { //userGroup.Use(handles.AccessJsMiddleware()) //显示用户登录主页 userGroup.GET( "/show" , handles.ShowUserLogin()) //用户登录 userGroup.POST( "/login" , handles.Validate(), //用户密码有效性验证 handles.HandleUserLogin()) //用户管理 userGroup.GET( "/manager/show" , handles.Cookie(), handles.ShowUserManager()) userGroup.POST( "/manager/modify" , handles.Cookie(), handles.HandleUserOperte()) } } |
三、中间件件的流转
gin提供了两个函数Abort()
和Next()
,配合着return
关键字用来跳转或者终止存在着业务逻辑关系的中间件。
.Next()
跳过当前的中间件,执行下一个中间件,待下一个中间件执行完后再回到当前next位置,执行后面的逻辑
.Abort()
终止该中间件的流程,如果不return
的话会继续执行后面的逻辑,但不再执行其他的中间件
结论
1. next()函数会跳过当前中间件中next()后的逻辑,当下一个中间件执行完成后再执行剩余的逻辑
2. abort()函数执行终止当前中间件以后的中间件执行,但是会执行当前中间件的后续逻辑
使用 Go 处理中间件 - 知乎
Gin框架系列03:换个姿势理解中间件 - Go语言中文网 - Golang中文社区
golang gin 中间件,返回结果 - 漫步的影子 - 博客园
gin使用中间件出错后不能用return终止,而应该使用Abort实现_katy的小乖的博客-优快云博客
使用 Go 处理中间件 - SegmentFault 思否