生命在折旧 發表於 2020-1-16 16:46:00

[go]gin中间件

<p>Go Web轻量级框架Gin学习系列:中间件使用详解</p>
<h2 id="gin中间件使用">gin中间件使用</h2>
<pre><code>- 中间件的作用:
        请求到达http请求处理方法之前,拦截请求
                认证
                权限校验
                限流
                数据过滤
                ip白名单
        处理完请求后,拦截响应,冰进行相应的处理
                统一添加响应头
                数据过滤


- 中间件加的位置
        全局加
        路由组加
        路由明细加
</code></pre>
<pre><code>- 默认使用了Logger(), Recovery()全局作用了两个中间件.
r:=gin.Default()

func Default() *Engine {
        debugPrintWARNINGDefault()
        engine := New()
        engine.Use(Logger(), Recovery())
        return engine
}


- gin自带默认有这些中间件
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包装成中间件
</code></pre>
<pre><code>//去除默认全局中间件
r := gin.New()//不带中间件
</code></pre>
<pre><code>- 全局中间件
func main() {
        r := gin.Default()

        r.Use(func(c *gin.Context) {
                fmt.Println("hello start")
        })

        r.GET("/", func(c *gin.Context) {
                c.JSON(200, gin.H{"name": "m1"})
        })

        r.Run()
}


- 或者
func M1(c *gin.Context) {
        fmt.Println("hello start")
}

func main() {
        r := gin.Default()

        r.Use(M1)

        r.GET("/", func(c *gin.Context) {
                c.JSON(200, gin.H{"name": "m1"})
        })

        r.Run()
}

</code></pre>
<pre><code>- 路由分组中使用中间件

func main() {
        r := gin.Default()
        v1 := r.Group("/v1", gin.Logger(), gin.Recovery())
        {
                v1.GET("/", func(c *gin.Context) {
                        c.JSON(200, gin.H{"name": "m1"})
                })
                v1.GET("/test", func(c *gin.Context) {
                        c.JSON(200, gin.H{"name": "m1 test"})
                })
        }
       
        r.Run()
}

</code></pre>
<pre><code>- 单个路由使用中间件

func main() {
        r := gin.Default()
        r.GET("/", gin.Recovery(), gin.Logger(), func(c *gin.Context) {
                c.JSON(200, gin.H{"name": "m1"})
        })
        r.Run()
}

</code></pre>
<pre><code>- 自定义中间件

//方法1
func MyMiddleware(c *gin.Context) {
        //中间件逻辑
        fmt.Println("hello")
}

func main() {
        r := gin.Default()

        r.Use(MyMiddleware)

        r.GET("/", func(c *gin.Context) {
                c.JSON(200, gin.H{"name": "m1"})
        })
        r.Run()
}



//方法2: 返回一个中间件函数

Gin框架自带的中间件方法,都是返回HandlerFunc类型
type HandlerFunc func(*Context)


func MyMiddleware() func(c *gin.Context) {
        //自定义逻辑
        fmt.Println("requesting...") //中间件不打印
        //返回中间件
        return func(c *gin.Context) {
                //中间件逻辑
                fmt.Println("test2")
        }
}


func main() {
        r := gin.Default()

        r.Use(MyMiddleware()) //加括号

        r.GET("/", func(c *gin.Context) {
                c.JSON(200, gin.H{"name": "m1"})
        })
        r.Run()
}
</code></pre>
<pre><code>- 值传递

func MyMiddleware(c *gin.Context) {
        c.Set("mykey", 10)
        c.Set("mykey2", "m1")
}
func main() {
        //自定义中间件
        r := gin.New()
        r.GET("", MyMiddleware, func(c *gin.Context) {
                mykey := c.GetInt("mykey") //我们知道设置进行的是整型,所以使用GetInt方法来获取
                mykey2 := c.GetString("mykey2")
                c.JSON(200, gin.H{
                        "mykey":mykey,
                        "mykey2": mykey2,
                })
        })
        r.Run()
}


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

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 mapinterface{})
func (c *Context) GetStringMapString(key string) (sms mapstring)
func (c *Context) GetStringMapStringSlice(key string) (smss map[]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
</code></pre>
<pre><code>//gin set get取参数 将值放在了context的keys键上(源码里context.go 叫: METADATA MANAGEMENT)

func main() {
        r := gin.Default()
        r.GET("/", func(c *gin.Context) {
                c.Set("name", "mm")
                fmt.Printf("%#v\n", c)
                c.JSON(200, gin.H{"name": "m1"})
        })
        r.Run()
}

&amp;gin.Context{
        writermem:gin.responseWriter{ResponseWriter:(*http.response)(0xc00024c1c0),
                                                                        size:-1,
                                                                        status:200
                                                                },
        Request:(*http.Request)(0xc000208100),
        Writer:(*gin.responseWriter)(0xc00020c410),
        Params:gin.Params(nil),
        handlers:gin.HandlersChain{(gin.HandlerFunc)(0x8fbd50),
        (gin.HandlerFunc)(0x8fccc0),
        (gin.HandlerFunc)(0x8fe720)},
        index:2,
        fullPath:"/",
        engine:(*gin.Engine)(0xc000222280),
        Keys:mapinterface {}{"name":"mm"},   //放在了这个map里
        Errors:gin.errorMsgs(nil),
        Accepted:[]string(nil),
        queryCac
        he:url.Values(nil),
        formCache:url.Values(nil)
}

</code></pre>
<pre><code>- 拦截器

func MyMiddleware(c *gin.Context){
    //请求前逻辑
    c.Next()
    //请求后逻辑
}

- gin内置的几个中断用户请求的方法: 返回200,但body里没数据
func (c *Context) Abort()
func (c *Context) AbortWithError(code int, err error) *Error
func (c *Context) AbortWithStatus(code int)

func (c *Context) AbortWithStatusJSON(code int, jsonObj interface{}) //中断请求后,返回json格式的数据

// 示例1:
func MyMiddleware(c *gin.Context) {
        c.Set("key", 1000) //请求前
        c.Next()
        c.JSON(http.StatusOK, c.GetInt("key")) //请求后
}
func main() {
        r := gin.New()
        r.GET("test", MyMiddleware, func(c *gin.Context) {
                k := c.GetInt("key")
                c.Set("key", k+2000)
        })
        r.Run()
}

</code></pre>
<h2 id="实例">实例</h2>
<h3 id="ginbasicauth中间件">gin.BasicAuth中间件</h3>
<pre><code>package main

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

//type HandlerFunc func(*Context)

// 模拟一些私人数据
var secrets = gin.H{
        "foo":    gin.H{"email": "foo@bar.com", "phone": "123433"},
        "austin": gin.H{"email": "austin@example.com", "phone": "666"},
        "lena":   gin.H{"email": "lena@guapa.com", "phone": "523443"},
}

func main() {

        r := gin.Default()

        r.GET("/", func(c *gin.Context) {
                c.JSON(200, secrets)
                //c.String(200, "index")
        })
        // 为/admin路由组设置auth
        // 路由组使用 gin.BasicAuth() 中间件
        // gin.Accounts 是 mapstring 的一种快捷方式
        authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{
                "foo":    "bar",
                "austin": "1234",
                "lena":   "hello2",
                "manu":   "4321",
        }))

        // /admin/secrets 端点
        // 触发 "localhost:8080/admin/secrets
        authorized.GET("/secrets", func(c *gin.Context) {
                // 获取用户,它是由 BasicAuth 中间件设置的
                user := c.MustGet(gin.AuthUserKey).(string)
                if secret, ok := secrets; ok {
                        c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret})
                } else {
                        c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("})
                }
        })

        // 监听并在 0.0.0.0:8080 上启动服务
        r.Run(":8080")
}
</code></pre>
<p>参考<br>
<img src="https://img2018.cnblogs.com/blog/1312420/202001/1312420-20200117091717026-1977177599.png" alt="" loading="lazy"></p>
<p>一文读懂HTTP Basic身份认证</p>
<pre><code>- http base认证

str=base64(admin:123456)
//request请求头部携带这个
Authorization: Basic YWRtaW46MTIzNDU2

- 步骤1:用户访问受限资源
GET /protected_docs HTTP/1.1
Host: 127.0.0.1:3000

- 步骤2:服务端返回401要求身份认证
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm=protected_docs

- 步骤3:用户发送认证请求
GET /protected_docs HTTP/1.1
Authorization: Basic Y2h5aW5ncDoxMjM0NTY=

Authorization首部的格式为Basic base64(userid:password)实际代码如下:
Buffer.from('chyingp:123456').toString('base64'); // Y2h5aW5ncDoxMjM0NTY=

- 步骤4:服务端验证请求
服务端收到用户的认证请求后,对请求进行验证。验证包含如下步骤:

    根据用户请求资源的地址,确定资源对应的realm。
    解析 Authorization 请求首部,获得用户名、密码。
    判断用户是否有访问该realm的权限。
    验证用户名、密码是否匹配。
一旦上述验证通过,则返回请求资源。如果验证失败,则返回401要求重新认证,或者返回403(Forbidden)。

缺陷:
1.密码明文传输
2.无法吊销认证
</code></pre>
<h3 id="中间件计算一次请求的耗时-cnext前置-后置">中间件计算一次请求的耗时: c.Next前置-后置</h3>
<pre><code>package main

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

//type HandlerFunc func(*Context)
func StatCost() gin.HandlerFunc {
    return func(c *gin.Context) {
      t := time.Now()

      //可以设置一些公共参数
      c.Set("example", "12345")
      //等其他中间件先执行
      c.Next()
      //获取耗时
      latency := time.Since(t)
      log.Printf("total cost time:%d us", latency/1000)
    }
}

func main() {
    //r := gin.New()
    r := gin.Default()
    r.Use(StatCost())

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

      // it would print: "12345"
      log.Println(example)
      c.JSON(http.StatusOK, gin.H{
            "message": "success",
      })
    })

    // Listen and serve on 0.0.0.0:8080
    r.Run()
}
</code></pre><br><br>
来源:https://www.cnblogs.com/iiiiiher/p/12202091.html
頁: [1]
查看完整版本: [go]gin中间件