个人建议:

gin 中间件

代码:

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"time"
)
//HandlerFunc
func indexHandler (c *gin.Context) {
	fmt.Println("index,,,")
	c.JSON(http.StatusOK,gin.H{
		"msg":"index",
	})

}
//定义一个中间件m1  一个统计耗时请求耗时的中间件
func m1(c *gin.Context ){
	fmt.Println("m1 in ...")
	//计时
	start :=time.Now()
	c.Next()//调用后续的处理函数
	//c.Abort()//阻止调用后续的处理函数
	cost:=time.Since(start)
	// 计算耗时
	fmt.Printf("cost:%v\n" ,cost)

	fmt.Println("m1 out ...")



}
func main()  {
	r:= gin.Default()
	//func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
	//	engine.RouterGroup.Use(middleware...)
	//	engine.rebuild404Handlers()
	//	engine.rebuild405Handlers()
	//	return engine
	//}
	r.Use(m1)//全局注册中间件函数
//GET(relativePath string, handlers ...HandlerFunc)
	r.GET("/index",indexHandler)
	r.GET("/shop", func(c *gin.Context) {
		c.JSON(http.StatusOK,gin.H{
			"msg":"shop",
		})
		
	})
	r.GET("/user",m1, func(c *gin.Context) {
		c.JSON(http.StatusOK,gin.H{
			"msg":"user",
		})

	})
	r.Run(":5555")

}

定义

//定义一个中间件m1  一个统计耗时请求耗时的中间件
func m1(c *gin.Context) {
	fmt.Println("m1 in ...")
	//计时
	start := time.Now()
	c.Next() //调用后续的处理函数
	//c.Abort()//阻止调用后续的处理函数
	cost := time.Since(start)
	// 计算耗时
	fmt.Printf("cost:%v\n", cost)

	fmt.Println("m1 out ...")

}

接下来分析

package main

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

//HandlerFunc
func indexHandler(c *gin.Context) {
	fmt.Println("index,,,")
	c.JSON(http.StatusOK, gin.H{
		"msg": "index",
	})

}

//定义一个中间件m1  一个统计耗时请求耗时的中间件
func m1(c *gin.Context) {
	fmt.Println("m1 in ...")
	//计时
	start := time.Now()
	c.Next() //调用后续的处理函数
	//c.Abort()//阻止调用后续的处理函数
	cost := time.Since(start)
	// 计算耗时
	fmt.Printf("cost:%v\n", cost)
	fmt.Println("m1 out ...")

}
//定义一个中间件m2  一个统计耗时请求耗时的中间件
func m2(c *gin.Context) {
	fmt.Println("m2 in ...")
	c.Next() //调用后续的处理函数
	fmt.Println("m2 out ...")

}
func main() {
	r := gin.Default()
	//func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
	//	engine.RouterGroup.Use(middleware...)
	//	engine.rebuild404Handlers()
	//	engine.rebuild405Handlers()
	//	return engine
	//}
	r.Use(m1,m2) //全局注册中间件函数m1,m2
	//GET(relativePath string, handlers ...HandlerFunc)
	r.GET("/index", indexHandler)
	r.GET("/shop", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"msg": "shop",
		})

	})
	r.GET("/user", m1, func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"msg": "user",
		})

	})
	r.Run(":5555")

}

image-20200814135122855

或者

image-20200814135218074

简单画出步骤就是

image-20200814135346805

当m2中

c.Next() //调用后续的处理函数
切换为
c.About()//阻止后续的处理函数

image-20200814135946227

现在再添加

//定义一个中间件m1  一个统计耗时请求耗时的中间件
func m2(c *gin.Context) {
	fmt.Println("m2 in ...")
	c.Abort() //组织后续的处理函数
	return
	fmt.Println("m2 out ...")

}

image-20200814140406124

中间件的真正使用:

//doCheck bool开关
func authMiddleware(doCheck bool)gin.HandlerFunc{
   //利用到了闭包的使用
   //连接数据库
   //或者一些其他准备工作
   return func(c *gin.Context) {
      if doCheck{
         //存放具体的处理逻辑
         //是否登录的判断
         //if 是登录用户
         //c.next()
         //else
         //c.about()
      }else{
         c.Next()

      }
      
   }

}

注册中间件

在gin框架中,我们可以为每个路由添加任意数量的中间件。

为全局路由注册

func main() {
	// 新建一个没有任何默认中间件的路由
	r := gin.New()
	// 注册一个全局中间件
	r.Use(StatCost())
	
	r.GET("/test", func(c *gin.Context) {
		name := c.MustGet("name").(string) // 从上下文取值
		log.Println(name)
		c.JSON(http.StatusOK, gin.H{
			"message": "Hello world!",
		})
	})
	r.Run()
}

为某个路由单独注册

// 给/test2路由单独注册中间件(可注册多个)
	r.GET("/test2", StatCost(), func(c *gin.Context) {
		name := c.MustGet("name").(string) // 从上下文取值
		log.Println(name)
		c.JSON(http.StatusOK, gin.H{
			"message": "Hello world!",
		})
	})

为路由组注册中间件

为路由组注册中间件有以下两种写法。

写法1:

shopGroup := r.Group("/shop", StatCost())
{
    shopGroup.GET("/index", func(c *gin.Context) {...})
    ...
}

写法2:

shopGroup := r.Group("/shop")
shopGroup.Use(StatCost())
{
    shopGroup.GET("/index", func(c *gin.Context) {...})
    ...
}

中间件注意事项

gin默认中间件

gin.Default()默认使用了LoggerRecovery中间件,其中:

  • Logger中间件将日志写入gin.DefaultWriter,即使配置了GIN_MODE=release
  • Recovery中间件会recover任何panic。如果有panic的话,会写入500响应码。

如果不想使用上面两个默认的中间件,可以使用gin.New()新建一个没有任何默认中间件的路由。

gin中间件中使用goroutine

当在中间件或handler中启动新的goroutine时,不能使用原始的上下文(c *gin.Context),必须使用其只读副本(c.Copy())。

运行多个服务

我们可以在多个端口启动服务,例如:

package main

import (
	"log"
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"golang.org/x/sync/errgroup"
)

var (
	g errgroup.Group
)

func router01() http.Handler {
	e := gin.New()
	e.Use(gin.Recovery())
	e.GET("/", func(c *gin.Context) {
		c.JSON(
			http.StatusOK,
			gin.H{
				"code":  http.StatusOK,
				"error": "Welcome server 01",
			},
		)
	})

	return e
}

func router02() http.Handler {
	e := gin.New()
	e.Use(gin.Recovery())
	e.GET("/", func(c *gin.Context) {
		c.JSON(
			http.StatusOK,
			gin.H{
				"code":  http.StatusOK,
				"error": "Welcome server 02",
			},
		)
	})

	return e
}

func main() {
	server01 := &http.Server{
		Addr:         ":8080",
		Handler:      router01(),
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
	}

	server02 := &http.Server{
		Addr:         ":8081",
		Handler:      router02(),
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
	}
   // 借助errgroup.Group或者自行开启两个goroutine分别启动两个服务
	g.Go(func() error {
		return server01.ListenAndServe()
	})

	g.Go(func() error {
		return server02.ListenAndServe()
	})

	if err := g.Wait(); err != nil {
		log.Fatal(err)
	}
}