无中间件
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)
}