gin系列-中间件
程序员文章站
2022-07-09 19:48:12
Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等 定义中间件 Gin中的中间件必须是一个gin.HandlerFunc类型 入门案例 注册中间件 在gin框架中 ......
gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等
定义中间件
gin中的中间件必须是一个gin.handlerfunc类型
入门案例
package main import ( "fmt" "github.com/gin-gonic/gin" "net/http" ) func indexhandler(c *gin.context) { fmt.println("index in ...") c.json(http.statusok, gin.h{ "msg": "indx", }) } //定义一个中间件 func m1(c *gin.context) { fmt.println("m1 in ....") } func main() { r := gin.default() //get(relativepath string, handlers ...handlerfunc) iroutes r.get("/index",m1,indexhandler) //r.get("/index", func(c *gin.context) { // c.json(http.statusok, gin.h{ // "msg": "indx", // }) //}) r.run(":9090") } [gin-debug] listening and serving http on :9090 m1 in .... index in ... [gin] 2020/04/21 - 15:21:31 |?[97;42m 200 ?[0m| 998.3µs | 127.0.0.1 |?[97;44m get ?[0m "/index"
package main import ( "fmt" "github.com/gin-gonic/gin" "net/http" "time" ) func indexhandler(c *gin.context) { fmt.println("index in ...") c.json(http.statusok, gin.h{ "msg": "indx", }) } //定义一个中间件:统计耗时 func m1(c *gin.context) { fmt.println("m1 in ....") //计时 start := time.now() c.next() //调用后续的处理函数 执行indexhandler函数 //c.abort() //阻止调用后续的处理函数 cost := time.since(start) fmt.println("cost:%v\n", cost) //输出 // m1 in .... //index in ... //cost:%v // 996.8µs } func main() { r := gin.default() //get(relativepath string, handlers ...handlerfunc) iroutes r.get("/index",m1,indexhandler) //先执行m1函数再执行indexhandler函数 //r.get("/index", func(c *gin.context) { // c.json(http.statusok, gin.h{ // "msg": "indx", // }) //}) r.run(":9090") }
注册中间件
在gin框架中,可以为每个路由添加任意数量的中间件。
为全局路由注册
package main import ( "fmt" "github.com/gin-gonic/gin" "net/http" "time" ) func indexhandler(c *gin.context) { fmt.println("index in ...") c.json(http.statusok, gin.h{ "msg": "indx", }) } //定义一个中间件:统计耗时 func m1(c *gin.context) { fmt.println("m1 in ....") //计时 start := time.now() c.next() //调用后续的处理函数 执行indexhandler函数 //c.abort() //阻止调用后续的处理函数 cost := time.since(start) fmt.println("cost:%v\n", cost) fmt.println("m1 out") //输出 // [gin-debug] listening and serving http on :9090 //m1 in .... //m2 in .... //index in ... //m2 out //cost:%v // 997.3µs //m1 out } func m2(c *gin.context) { fmt.println("m2 in ....") c.next() //调用后续的处理函数 fmt.println("m2 out") } func main() { r := gin.default() r.use(m1,m2) //全局注册中间件函数m1,m2 洋葱模型 类似递归调用 //get(relativepath string, handlers ...handlerfunc) iroutes //r.get("/index",m1,indexhandler) //先执行m1函数再执行indexhandler函数 r.get("/index",indexhandler) r.get("/shop", func(c *gin.context) { c.json(http.statusok, gin.h{ "msg": "index", }) }) r.get("/user", func(c *gin.context) { c.json(http.statusok, gin.h{ "msg": "index", }) }) r.run(":9090") }
package main import ( "fmt" "github.com/gin-gonic/gin" "net/http" "time" ) func indexhandler(c *gin.context) { fmt.println("index in ...") c.json(http.statusok, gin.h{ "msg": "indx", }) } //定义一个中间件:统计耗时 func m1(c *gin.context) { fmt.println("m1 in ....") //计时 start := time.now() c.next() //调用后续的处理函数 执行indexhandler函数 //c.abort() //阻止调用后续的处理函数 cost := time.since(start) fmt.println("cost:%v\n", cost) fmt.println("m1 out") //输出 // [gin-debug] listening and serving http on :9090 //m1 in .... //m2 in .... //m2 out //cost:%v // 997.8µs //m1 out } func m2(c *gin.context) { fmt.println("m2 in ....") //c.next() //调用后续的处理函数 c.abort() //阻止后续调用 //return //return 立即结束m2函数 //m1 in .... //m2 in .... //cost:%v // 0s //m1 out fmt.println("m2 out") } //func authmiddleware(c *gin.context) { //通常写成闭包 // //是否登陆的判断 // //if 是登陆用户 // //c.next() // //else // //c.abort() //} func authmiddleware(docheck bool)gin.handlerfunc { //开关注册 //连接数据库 //或着其他准备工作 return func(c *gin.context) { if docheck { //是否登陆的判断 //if 是登陆用户 //c.next() //else //c.abort() } else { c.next() } } } func main() { r := gin.default() r.use(m1,m2,authmiddleware(true)) //全局注册中间件函数m1,m2 洋葱模型 类似递归调用 //get(relativepath string, handlers ...handlerfunc) iroutes //r.get("/index",m1,indexhandler) //先执行m1函数再执行indexhandler函数 r.get("/index",indexhandler) r.get("/shop", func(c *gin.context) { c.json(http.statusok, gin.h{ "msg": "index", }) }) r.get("/user", func(c *gin.context) { c.json(http.statusok, gin.h{ "msg": "index", }) }) r.run(":9090") }
为某个路由单独注册
import ( "fmt" "github.com/gin-gonic/gin" "net/http" "time" ) //定义一个中间件:统计耗时 func m1(c *gin.context) { fmt.println("m1 in ....") //计时 start := time.now() c.next() //调用后续的处理函数 执行indexhandler函数 //c.abort() //阻止调用后续的处理函数 cost := time.since(start) fmt.println("cost:%v\n", cost) fmt.println("m1 out") } func main() { r := gin.default() r.get("/user", m1, func(c *gin.context) { c.json(http.statusok, gin.h{ "msg": "index", }) }) //m1 in .... //cost:%v // 0s //m1 out r.run(":9090") }
import ( "fmt" "github.com/gin-gonic/gin" "net/http" "time" ) func indexhandler(c *gin.context) { fmt.println("index in ...") c.json(http.statusok, gin.h{ "msg": "indx", }) } //定义一个中间件:统计耗时 func m1(c *gin.context) { fmt.println("m1 in ....") //计时 start := time.now() c.next() //调用后续的处理函数 执行indexhandler函数 //c.abort() //阻止调用后续的处理函数 cost := time.since(start) fmt.println("cost:%v\n", cost) fmt.println("m1 out") } func m2(c *gin.context) { fmt.println("m2 in ....") //c.next() //调用后续的处理函数 c.abort() //阻止后续调用 //return //return 立即结束m2函数 //m1 in .... //m2 in .... //cost:%v // 0s //m1 out fmt.println("m2 out") } func main() { r := gin.default() r.get("/user", m1,m2, func(c *gin.context) { //可以单独多个路由 c.json(http.statusok, gin.h{ "msg": "index", }) }) //[gin-debug] listening and serving http on :9090 //m1 in .... //m2 in .... //m2 out //cost:%v // 0s //m1 out r.run(":9090") }
为路由组注册中间件
func main() { //路由组注册中间件方法1: xxgroup := r.group("/xx", authmiddleware(true)) { xxgroup.get("/index", func(c *gin.context) { c.json(http.statusok, gin.h{"msg":"xxgroup"}) }) } //路由组注册中间件方法2: xx2group := r.group("/xx") xx2group.use(authmiddleware(true)) { xxgroup.get("/index", func(c *gin.context) { c.json(http.statusok, gin.h{"msg":"xxgroup"}) }) } r.run(":9090") }
跨中间件存取值
package main import ( "fmt" "github.com/gin-gonic/gin" "net/http" "time" ) func indexhandler(c *gin.context) { fmt.println("index in ...") name, ok := c.get("name") //从上下文中取值,跨中间件存取值 if !ok { name = "匿名用户" } c.json(http.statusok, gin.h{ "msg": name, }) } //定义一个中间件:统计耗时 func m1(c *gin.context) { fmt.println("m1 in ....") //计时 start := time.now() c.next() //调用后续的处理函数 执行indexhandler函数 //c.abort() //阻止调用后续的处理函数 cost := time.since(start) fmt.println("cost:%v\n", cost) fmt.println("m1 out") } func m2(c *gin.context) { fmt.println("m2 in ....") c.set("name","zisefeizhu") //在上下文中设置c的值 fmt.println("m2 out") } func authmiddleware(docheck bool)gin.handlerfunc { //开关注册 //连接数据库 //或着其他准备工作 return func(c *gin.context) { if docheck { //是否登陆的判断 //if 是登陆用户 c.next() //else //c.abort() } else { c.next() } } } func main() { r := gin.default() r.use(m1,m2,authmiddleware(true)) //全局注册中间件函数m1,m2 洋葱模型 类似递归调用 //get(relativepath string, handlers ...handlerfunc) iroutes //r.get("/index",m1,indexhandler) //先执行m1函数再执行indexhandler函数 r.get("/index",indexhandler) r.run(":9090") }
中间件注意事项
gin.default()
gin.default()默认使用了logger和recovery中间件,其中:logger中间件将日志写入gin.defaultwriter,即使配置了gin_mode=release。recovery中间件会recover任何panic。如果有panic的话,会写入500响应码。如果不想使用上面两个默认的中间件,可以使用gin.new()新建一个没有任何默认中间件的路由。
package main import ( "fmt" "github.com/gin-gonic/gin" "net/http" "time" ) func indexhandler(c *gin.context) { fmt.println("index in ...") name, ok := c.get("name") //从上下文中取值,跨中间件存取值 if !ok { name = "匿名用户" } c.json(http.statusok, gin.h{ "msg": name, }) } //定义一个中间件:统计耗时 func m1(c *gin.context) { fmt.println("m1 in ....") //计时 start := time.now() c.next() //调用后续的处理函数 执行indexhandler函数 //c.abort() //阻止调用后续的处理函数 cost := time.since(start) fmt.println("cost:%v\n", cost) fmt.println("m1 out") } func m2(c *gin.context) { fmt.println("m2 in ....") c.set("name","zisefeizhu") //在上下文中设置c的值 fmt.println("m2 out") } func authmiddleware(docheck bool)gin.handlerfunc { //开关注册 //连接数据库 //或着其他准备工作 return func(c *gin.context) { if docheck { //是否登陆的判断 //if 是登陆用户 c.next() //else //c.abort() } else { c.next() } } } func main() { //r := gin.default() //默认使用logger()和recovery()中间件 r := gin.new() r.use(m1,m2,authmiddleware(true)) //全局注册中间件函数m1,m2 洋葱模型 类似递归调用 r.get("/index",indexhandler) r.get("/shop", func(c *gin.context) { c.json(http.statusok, gin.h{ "msg": "index", }) }) r.get("/user", func(c *gin.context) { c.json(http.statusok, gin.h{ "msg": "index", }) }) r.run(":9090") } [gin-debug] listening and serving http on :9090 m1 in .... m2 in .... m2 out index in ... cost:%v 1.0137ms m1 out
gin中间件中使用goroutine
当在中间件或handler中启动新的goroutine时,不能使用原始的上下文(c *gin.context),必须使用其只读副本(c.copy())。
//定义一个中间件:统计耗时 func m1(c *gin.context) { fmt.println("m1 in ....") //计时 start := time.now() go funcxx(c.copy()) //在funcxx中只能使用c的拷贝 c.next() //调用后续的处理函数 执行indexhandler函数 //c.abort() //阻止调用后续的处理函数 cost := time.since(start) fmt.println("cost:%v\n", cost) fmt.println("m1 out") }