(摘) Gin 中间件

声明:内容源自网络,版权归原作者所有。若有侵权请在网页聊天中联系我

无中间件

func main() {
    //创建一个无中间件路由
    router := gin.New()
    router.Run()
}

默认中间件

func main() {
    // 默认启动方式,包含 Logger、Recovery 中间件
    router:=gin.Default()
    router.GET("/", func(context *gin.Context) {
        context.JSON(200,gin.H{
            "goods_name":"牛奶",
        })
    })
    router.Run()
}

自定义中间件

//自定义中间件第1种定义方式
func CustomRouterMiddle1(c *gin.Context)  {
	t := time.Now()
	fmt.Println("我是自定义中间件第1种定义方式---请求之前")
	//在gin上下文中定义一个变量
	c.Set("example", "CustomRouterMiddle1")
	//请求之前
	c.Next()
	fmt.Println("我是自定义中间件第1种定义方式---请求之后")
	//请求之后
	//计算整个请求过程耗时
	t2 := time.Since(t)
	log.Println(t2)

}

//自定义中间件第2种定义方式
func CustomRouterMiddle2() gin.HandlerFunc{
	return func(c *gin.Context) {
		t := time.Now()
		fmt.Println("我是自定义中间件第2种定义方式---请求之前")		
		c.Set("example", "CustomRouterMiddle2") //在gin上下文中定义一个变量
		c.Next()
		fmt.Println("我是自定义中间件第2种定义方式---请求之后")
		t2 := time.Since(t)
		log.Println(t2)
	}
}



func main() {
	
	r := gin.New()
	
	//测试时下面两个中间件选择一个,注释一个
	r.Use(CustomRouterMiddle1)
	r.Use(CustomRouterMiddle2())

	r.GET("/test", func(c *gin.Context) {
		example := c.MustGet("example").(string)
		log.Println(example)
	})

	// 监听本地8080端口
	r.Run(":8080")
}

路由中间件

func RouterMiddle1(c *gin.Context)  {
	fmt.Println("我是路由中间件1")
}

func RouterMiddle2(c *gin.Context)  {
	fmt.Println("我是路由中间件2")
}

func oneRouterMiddleHandle() gin.HandlerFunc{
	return func(c *gin.Context) {
		fmt.Println("我是业务处理函数")
	}
}

func main() {	
	router := gin.New() //创建一个无中间件路由	
	router.GET("/oneRouterMiddle", RouterMiddle1,RouterMiddle2,oneRouterMiddleHandle()) // 对于每个路由中间件,您可以添加任意数量的路由中间件	
	router.Run()  // 默认监听本地 0.0.0.0:8080 即localhost:8080 或 127.0.0.1:8080
}
package main

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

func main() {
	router := gin.Default()
	router.GET("/", gin.Recovery(), gin.Logger(), func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"msg": "single route"})
	})

	router.Run(":8080")
}

路由组中间件

func GroupRouterGoodsMiddle1(c *gin.Context)  {
	fmt.Println("我是goods路由组中间件1")
}

func GroupRouterGoodsMiddle2(c *gin.Context) {
	fmt.Println("我是goods路由组中间件2")
}

func GroupRouterOrderMiddle1(c *gin.Context) {
	fmt.Println("我是order路由组中间件1")
}

func GroupRouterOrderMiddle2(c *gin.Context) {
	fmt.Println("我是order路由组中间件2")
}

func main() {
	//创建一个无中间件路由
	router := gin.New()
	router.Use(gin.Logger())

	//第1种路由组使用方式 可以添加多个处理函数 但是不知道为什么 官方举例的这第一种方式用不了
	router.Group("/goods", GroupRouterGoodsMiddle1, GroupRouterGoodsMiddle2)
	router.GET("/goods/add", func(context *gin.Context) {
		fmt.Println("/goods/add")
	})


	//第2种路由组使用方式
	orderGroup := router.Group("/order")
	orderGroup.Use(GroupRouterOrderMiddle1, GroupRouterOrderMiddle2)
	{
		orderGroup.GET("/add", func(context *gin.Context) {
			fmt.Println("/order/add")

		})

		orderGroup.GET("/del", func(context *gin.Context) {
			fmt.Println("/order/del")

		})

		//orderGroup下再嵌套一个testGroup
		testGroup:=orderGroup.Group("/test", func(context *gin.Context) {
			fmt.Println("order/test下的中间件")
		})

		testGroup.GET("/test1", func(context *gin.Context) {
			fmt.Println("order/test/test1的函数")
		})
	}

	router.Run()
}
package main

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

func main() {
	router := gin.Default()
	v1 := router.Group("/v1", gin.Logger(), gin.Recovery())
	{
		v1.GET("/test1", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{"res": "test1"})
		})
		v1.GET("/test2", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{"res": "test2"})
		})
	}

	router.Run(":8080")
}

全局中间件

func GlobalMiddle(c *gin.Context){
	fmt.Println("我是全局中间件")
}

func main() {
//创建一个无中间件路由
router := gin.New()

//使用自定义的全局中间件
router.Use(GlobalMiddle)

router.GET("/", func(context *gin.Context) {
	fmt.Println("我是/")
})

router.Run()
}
package main

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

func main() {
	router := gin.Default()
	router.Use(func(c *gin.Context) {
		fmt.Println("hello middleware")
	})

	router.GET("/", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"status": "OK"})
	})

	router.Run(":8080")
}

执行顺序: 全局中间件 > 路由组中间件 > 路由中间件

内置中间件

func BasicAuth(accounts Accounts) HandlerFunc
func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc
func Bind(val interface{}) HandlerFunc //拦截请求参数并进行绑定
func ErrorLogger() HandlerFunc       //错误日志处理
func ErrorLoggerT(typ ErrorType) HandlerFunc //自定义类型的错误日志处理
func Logger() HandlerFunc //日志记录
func LoggerWithConfig(conf LoggerConfig) HandlerFunc
func LoggerWithFormatter(f LogFormatter) HandlerFunc
func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc
func Recovery() HandlerFunc
func RecoveryWithWriter(out io.Writer) HandlerFunc
func WrapF(f http.HandlerFunc) HandlerFunc //将http.HandlerFunc包装成中间件
func WrapH(h http.Handler) HandlerFunc //将http.Handler包装成中间件

传参

//gin set get取参数
func (c *Context) Set(key string, value interface{})
func (c *Context) Get(key string) (value interface{}, exists bool) //判断key是否存在 c.Get

func (c *Context) GetBool(key string) (b bool)
func (c *Context) GetDuration(key string) (d time.Duration)
func (c *Context) GetFloat64(key string) (f64 float64)
func (c *Context) GetInt(key string) (i int)
func (c *Context) GetInt64(key string) (i64 int64)
func (c *Context) GetString(key string) (s string)
func (c *Context) GetStringMap(key string) (sm map[string]interface{})
func (c *Context) GetStringMapString(key string) (sms map[string]string)
func (c *Context) GetStringMapStringSlice(key string) (smss map[string][]string)
func (c *Context) GetStringSlice(key string) (ss []string)
func (c *Context) GetTime(key string) (t time.Time)

func (c *Context) MustGet(key string) interface{} //必须有, 否则panic
package main

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

func Middleware1(c *gin.Context) {
    // 可以通过上下文对象,设置一些依附在上下文对象里面的键/值数据
	c.Set("key1", 123)
	c.Set("key2", "renwoxing")

}
func main() {
	router := gin.New()
	router.GET("/", Middleware1, func(c *gin.Context) {
		key1 := c.GetInt("key1")
		key2 := c.GetString("key2")
		c.JSON(http.StatusOK, gin.H{
			"key1": key1,
			"key2": key2,
		})
	})
	router.Run(":8080")
}

gin.BasicAutn

package main

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

// 模拟数据
var secrets = gin.H{
	"zhangsan": gin.H{"email": "zhangsan@example.com", "phone": "123456"},
	"lisi":     gin.H{"email": "lisi@example.com", "phone": "123456"},
	"wangwu":   gin.H{"email": "wangwu@example.com", "phone": "123456"},
}

func main() {
	router := gin.Default()
	router.GET("/", func(c *gin.Context) {
		c.JSON(http.StatusOK, secrets)
	})
	// 为/admin路由组设置auth, 路由组使用 gin.BasicAuth() 中间件
	auth := router.Group("/admin", gin.BasicAuth(gin.Accounts{
		"zhangsan": "zhangsan",
		"lisi":     "lisi",
		"wangwu":   "wangwu",
	}))

	// /admin/secrets 端点
	auth.GET("/secrets", func(c *gin.Context) {
		//获取用户名
		user := c.MustGet(gin.AuthUserKey).(string)
		if secret, ok := secrets[user]; ok {
			c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret})
		} else {
			c.JSON(http.StatusOK, gin.H{"user": user, "secret": "No secret"})
		}

	})
	router.Run(":8080")
}
func main() {
    r := gin.Default()

    r.GET("/", func(c *gin.Context) {
        c.JSON(200, "首页")
    })

    adminGroup := r.Group("/admin")
    adminGroup.Use(gin.BasicAuth(gin.Accounts{
        "admin": "123456",
    }))

    adminGroup.GET("/index", func(c *gin.Context) {
        c.JSON(200, "后台首页")
    })

    r.Run(":8080")
}

常用中间件

日志

// 日志记录到文件
func (d *Handler) LoggerToFile() gin.HandlerFunc {

	return func(c *gin.Context) {
		// 开始时间
		startTime := time.Now()

		// 处理请求
		c.Next()

		// 结束时间
		endTime := time.Now()

		// 执行时间
		latencyTime := endTime.Sub(startTime)

		// 请求方式
		reqMethod := c.Request.Method

		// 请求路由
		reqUri := c.Request.RequestURI

		// 状态码
		statusCode := c.Writer.Status()

		// 请求IP
		clientIP := c.ClientIP()

		//// 日志格式
		//fmt.Printf("%s [INFO] %s %s %3d %13v %15s \r\n",
		//	startTime.Format("2006-01-02 15:04:05"),
		//	reqMethod,
		//	reqUri,
		//	statusCode,
		//	latencyTime,
		//	clientIP,
		//)

		log.Infof("%s %s %3d %13v %15s",
			reqMethod,
			reqUri,
			statusCode,
			latencyTime,
			clientIP)

		if c.Request.Method != "GET" && c.Request.Method != "OPTIONS" && conf.Conf.LoggerConfig.EnabledDB {
			d.SetDBOperLog(c, clientIP, statusCode, reqUri, reqMethod, latencyTime)
		}
	}
}

自定义异常处理

func (d *Handler) CustomError(c *gin.Context) {
	defer func() {
		if err := recover(); err != nil {

			if c.IsAborted() {
				c.Status(200)
			}
			switch errStr := err.(type) {
			case string:
				p := strings.Split(errStr, "#")
				if len(p) == 3 && p[0] == "CustomError" {
					statusCode, e := strconv.Atoi(p[1])
					if e != nil {
						break
					}
					c.Status(statusCode)
					fmt.Println(
						time.Now().Format("2006-01-02 15:04:05"),
						"[ERROR]",
						c.Request.Method,
						c.Request.URL,
						statusCode,
						c.Request.RequestURI,
						c.ClientIP(),
						p[2],
					)
					c.JSON(http.StatusOK, gin.H{
						"code": statusCode,
						"msg":  p[2],
					})
				}
			default:
				panic(err)
			}
		}
	}()
	c.Next()
}

请求id

func (d *Handler) RequestId() gin.HandlerFunc {
	return func(c *gin.Context) {
		// Check for incoming header, use it if exists
		requestId := c.Request.Header.Get("X-Request-Id")

		// Create request id with UUID4
		if requestId == "" {
			u4 := uuid.NewV4()
			requestId = u4.String()
		}

		// Expose it for use in the application
		c.Set("X-Request-Id", requestId)

		// Set X-Request-Id header
		c.Writer.Header().Set("X-Request-Id", requestId)
		c.Next()
	}
}

nocache

// NoCache is a middleware function that appends headers
// to prevent the client from caching the HTTP response.
func (d *Handler) NoCache(c *gin.Context) {
	c.Header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value")
	c.Header("Expires", "Thu, 01 Jan 1970 00:00:00 GMT")
	c.Header("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
	c.Next()
}

跨域

//Options is a middleware function that appends headers
// for options requests and aborts then exits the middleware
// chain and ends the request.
func (d *Handler) Options(c *gin.Context) {
	if c.Request.Method != "OPTIONS" {
		c.Next()
	} else {
		c.Header("Access-Control-Allow-Origin", "*")
		c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
		c.Header("Access-Control-Allow-Headers", "lang,X-DEVICE-ID,X-APP-VERSION,X-CHANNEL,authorization, origin, content-type, accept,X-TENANT-CODE,sign,time")
		c.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS")
		c.Header("Content-Type", "application/json")
		c.AbortWithStatus(200)
	}
}

Secure

// Secure is a middleware function that appends security
// and resource access headers.
func (d *Handler) Secure(c *gin.Context) {
	c.Header("Access-Control-Allow-Origin", "*")
	//c.Header("X-Frame-Options", "DENY")
	c.Header("X-Content-Type-Options", "nosniff")
	c.Header("X-XSS-Protection", "1; mode=block")
	if c.Request.TLS != nil {
		c.Header("Strict-Transport-Security", "max-age=31536000")
	}

	// Also consider adding Content-Security-Policy headers
	// c.Header("Content-Security-Policy", "script-src 'self' https://cdnjs.cloudflare.com")
}

限流

func (d *Handler) Limiter(ctx *gin.Context) {
	now := time.Now().UnixNano()
	key := "REDIS_LIMITER"
	userCntKey := fmt.Sprint(constant.ImApiRedisPrefix, ctx.ClientIP(), ":", key)

	//五秒限流
	var limit int64 = 10
	dura := time.Second * 60
	//删除有序集合中的五秒之前的数据
	d.Dao.Redis.ZRemRangeByScore(ctx, userCntKey,
		"0",
		fmt.Sprint(now-(dura.Nanoseconds()))).Result()

	reqs, _ := d.Dao.Redis.ZCard(ctx, userCntKey).Result()

	if reqs >= limit {
		ctx.AbortWithStatusJSON(http.StatusTooManyRequests, gin.H{
			"status":  http.StatusTooManyRequests,
			"message": "too many request",
		})
		return
	}

	ctx.Next()
	d.Dao.Redis.ZAddNX(ctx, userCntKey, &redis.Z{Score: float64(now), Member: float64(now)})
	d.Dao.Redis.Expire(ctx, userCntKey, dura)
}