• Gin框架中间件


    Gin框架中间件

    Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。

    image-20211118214028476

    一、定义中间件

    Gin中的中间件必须是一个gin.HandlerFunc类型。例如我们像下面的代码一样定义一个统计请求耗时的中间件。

    package main
    
    import (
    	"fmt"
    	"github.com/gin-gonic/gin"
    	"net/http"
    	"time"
    )
    
    func index(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() // 阻止调用后续处理的函数
    	end := time.Since(start)
    	fmt.Printf("消耗时间time:%v\n", end)
    	fmt.Println("m1 out ....")
    
    }
    func main() {
    	r := gin.Default()
    
    	r.GET("/index", m1, index)
    	r.Run(":9999")
    
    }
    
    

    image-20211118220119559

    image-20211118220054724

    二、定义局部中间件

    在每次请求中添加中间件

    package main
    
    import (
    	"fmt"
    	"github.com/gin-gonic/gin"
    	"net/http"
    	"time"
    )
    
    func index(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() // 阻止调用后续处理的函数
    	end := time.Since(start)
    	fmt.Printf("消耗时间time:%v\n", end)
    	fmt.Println("m1 out ....")
    
    }
    func main() {
    	r := gin.Default()
    
    	r.GET("/index", m1, index)
    	// 局部中间件
    	r.GET("/home", m1, func(c *gin.Context) {
    		c.JSON(http.StatusOK, gin.H{
    			"msg":"home",
    		})
    	})
    	r.GET("/add", m1, func(c *gin.Context) {
    		c.JSON(http.StatusOK, gin.H{
    			"msg":"add",
    		})
    	})
    	r.Run(":9999")
    
    }
    
    

    image-20211118220751236

    三、定义全局中间件

    package main
    
    import (
    	"fmt"
    	"github.com/gin-gonic/gin"
    	"net/http"
    	"time"
    )
    
    func index(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() // 阻止调用后续处理的函数
    	end := time.Since(start)
    	fmt.Printf("消耗时间time:%v\n", end)
    	fmt.Println("m1 out ....")
    
    }
    func main() {
    	r := gin.Default()
    
    	// 全局注册中间
    	r.Use(m1)
    	r.GET("/index", index)
    	// 局部中间件
    	r.GET("/home", func(c *gin.Context) {
    		c.JSON(http.StatusOK, gin.H{
    			"msg": "home",
    		})
    	})
    	r.GET("/add", func(c *gin.Context) {
    		c.JSON(http.StatusOK, gin.H{
    			"msg": "add",
    		})
    	})
    	r.Run(":9999")
    
    }
    
    

    image-20211118220920806

    三、定义多个中间件

    package main
    
    import (
    	"fmt"
    	"github.com/gin-gonic/gin"
    	"net/http"
    	"time"
    )
    
    func index(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() // 阻止调用后续处理的函数
    	end := time.Since(start)
    	fmt.Printf("消耗时间time:%v\n", end)
    	fmt.Println("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)
    	r.GET("/index", index)
    	// 局部中间件
    	r.GET("/home", func(c *gin.Context) {
    		c.JSON(http.StatusOK, gin.H{
    			"msg": "home",
    		})
    	})
    	r.GET("/add", func(c *gin.Context) {
    		c.JSON(http.StatusOK, gin.H{
    			"msg": "add",
    		})
    	})
    	r.Run(":9999")
    
    }
    
    

    image-20211118221347175

    image-20211118221403410

    image-20211118221650300

    image-20211118221627386

    四、Abort

    package main
    
    import (
    	"fmt"
    	"github.com/gin-gonic/gin"
    	"net/http"
    	"time"
    )
    
    func index(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() // 调用后续处理的函数
    	end := time.Since(start)
    	fmt.Printf("消耗时间time:%v\n", end)
    	fmt.Println("m1 out ....")
    
    }
    
    func m2(c *gin.Context) {
    	fmt.Println("m2 in ....")
    
    	c.Abort() //  阻止调用后续处理的函数
    	return // 后面m2将不会在执行
    	fmt.Println("m2 out ....")
    
    }
    func main() {
    	r := gin.Default()
    
    	// 全局注册中间
    	r.Use(m1, m2)
    	r.GET("/index", index)
    	// 局部中间件
    	r.GET("/home", func(c *gin.Context) {
    		c.JSON(http.StatusOK, gin.H{
    			"msg": "home",
    		})
    	})
    	r.GET("/add", func(c *gin.Context) {
    		c.JSON(http.StatusOK, gin.H{
    			"msg": "add",
    		})
    	})
    	r.Run(":9999")
    
    }
    
    

    image-20211118222205118

    image-20211118222157465

    image-20211118222407006

    image-20211118222349970

    中间件return语句

    image-20211118222711540

    image-20211118222639299

    五、模拟认证

    package main
    
    import (
    	"fmt"
    	"github.com/gin-gonic/gin"
    	"net/http"
    	"time"
    )
    
    func index(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() // 阻止调用后续处理的函数
    	end := time.Since(start)
    	fmt.Printf("消耗时间time:%v\n", end)
    	fmt.Println("m1 out ....")
    
    }
    
    func m2(c *gin.Context) {
    	fmt.Println("m2 in ....")
    
    	c.Next() // 调用后续处理的函数
    
    	fmt.Println("m2 out ....")
    
    }
    
    // 认证中间件
    //func authMiddleware(c *gin.Context) {
    //	// 是否登录判断
    //	username := c.Query("username")
    //	// 判断是否登录用户
    //	if username == "RandySun" {
    //
    //		c.Next()
    //	} else {
    //		// 认证失败
    //		c.JSON(http.StatusUnauthorized, gin.H{
    //			"msg": "没有权限",
    //		})
    //		c.Abort()
    //	}
    //}
    
    // 通过闭包认证中间件
    func authMiddleware(doCheck bool) gin.HandlerFunc {
    	// 连接数据库
    	// 或者其他准备工作
    	return func(c *gin.Context) {
    		// 是否登录判断
    		username := c.Query("username")
    		// 判断是否登录用户
    		if doCheck {
    			if username == "RandySun" && doCheck {
    				c.Next()
    			} else {
    				// 认证失败
    				c.JSON(http.StatusUnauthorized, gin.H{
    					"msg": "没有权限",
    				})
    				c.Abort()
    			}
    
    		} else {
    			// 放行认证
    			c.Next()
    		}
    
    	}
    }
    
    func main() {
    	r := gin.Default()
    
    	// 全局注册中间
    	r.Use(m1, m2, authMiddleware(true))
    	r.GET("/index", index)	
    	r.Run(":9999")
    
    }
    
    

    image-20211118224430900

    image-20211118224443593

    六、为路由组注册中间件

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

    写法1:

    package main
    
    import (
    	"fmt"
    	"github.com/gin-gonic/gin"
    	"net/http"
    	"time"
    )
    
    func index(c *gin.Context) {
    	fmt.Println("index")
    	c.JSON(http.StatusOK, gin.H{
    		"msg": "index",
    	})
    }
    
    
    // 通过闭包认证中间件
    func authMiddleware(doCheck bool) gin.HandlerFunc {
    	// 连接数据库
    	// 或者其他准备工作
    	return func(c *gin.Context) {
    		// 是否登录判断
    		username := c.Query("username")
    		// 判断是否登录用户
    		if doCheck {
    			if username == "RandySun" && doCheck {
    				c.Next()
    			} else {
    				// 认证失败
    				c.JSON(http.StatusUnauthorized, gin.H{
    					"msg": "没有权限",
    				})
    				c.Abort()
    			}
    
    		} else {
    			// 放行认证
    			c.Next()
    		}
    
    	}
    }
    
    func main() {
    	r := gin.Default()
    	// 为路由组添加中间件
    	shopGroup := r.Group("/shop", authMiddleware(true))
    	{
    		shopGroup.GET("/index", func(c *gin.Context) {
    			c.JSON(http.StatusOK, gin.H{
    				"mgs": "shop index",
    			})
    		})
    
    	}
    
    	r.Run(":9999")
    
    }
    
    

    写法2:

    package main
    
    import (
    	"fmt"
    	"github.com/gin-gonic/gin"
    	"net/http"
    	"time"
    )
    
    func index(c *gin.Context) {
    	fmt.Println("index")
    	c.JSON(http.StatusOK, gin.H{
    		"msg": "index",
    	})
    }
    
    
    // 通过闭包认证中间件
    func authMiddleware(doCheck bool) gin.HandlerFunc {
    	// 连接数据库
    	// 或者其他准备工作
    	return func(c *gin.Context) {
    		// 是否登录判断
    		username := c.Query("username")
    		// 判断是否登录用户
    		if doCheck {
    			if username == "RandySun" && doCheck {
    				c.Next()
    			} else {
    				// 认证失败
    				c.JSON(http.StatusUnauthorized, gin.H{
    					"msg": "没有权限",
    				})
    				c.Abort()
    			}
    
    		} else {
    			// 放行认证
    			c.Next()
    		}
    
    	}
    }
    
    func main() {
    	r := gin.Default()
    	shopGroup := r.Group("/shop")
        
    	// 为路由组添加中间件
    	shopGroup.Use(authMiddleware(true))
    	{
    		shopGroup.GET("/index", func(c *gin.Context) {
    			c.JSON(http.StatusOK, gin.H{
    				"mgs": "shop index",
    			})
    		})
    
    	}
    	r.Run(":9999")
    
    }
    
    

    七、中间件取值

    package main
    
    import (
    	"fmt"
    	"github.com/gin-gonic/gin"
    	"net/http"
    	"time"
    )
    
    func index(c *gin.Context) {
    	fmt.Println("index")
    	name, _ := c.Get("name")
    	// 跨中间件取值
    	fmt.Println("获取在中间中设置的值name:", name)
    	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() // 阻止调用后续处理的函数
    	end := time.Since(start)
    	fmt.Printf("消耗时间time:%v\n", end)
    	fmt.Println("m1 out ....")
    
    }
    
    func m2(c *gin.Context) {
    	fmt.Println("m2 in ....")
    	// 在中间中设置值
    	c.Set("name", "randySun")
    
    	c.Next() // 调用后续处理的函数
    	fmt.Println("m2 out ....")
    
    }
    
    func main() {
    	r := gin.Default()
    
    	// 全局注册中间
    	r.Use(m1, m2)
    	r.GET("/index", index)
    	
    	r.Run(":9999")
    
    }
    
    

    image-20211118230215271

    八、中间件

    在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!",
    		})
    	})
    

    中间件注意事项

    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)
    	}
    }
    
  • 相关阅读:
    数据的独立同分布检验
    基于密度聚类的DBSCAN和kmeans算法比较
    Python 爬虫笔记、多线程、xml解析、基础笔记(不定时更新)
    多进程之multiprocessing模块、守护进程、互斥锁
    程序与进程的区别,并发与并行的区别,多进程的实现原理
    并发编程之守护进程
    MySQL帮助文档的使用
    MySQL操作之DCL
    MySQL操作之DML
    MySQL操作之DDL
  • 原文地址:https://www.cnblogs.com/randysun/p/15626706.html
Copyright © 2020-2023  润新知