前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >gorilla-context深入学习

gorilla-context深入学习

作者头像
陌无崖
发布2019-08-16 17:24:50
4630
发布2019-08-16 17:24:50
举报

点击蓝字关注我吧

导语

做过web开发的同学肯定都知道,我们经常使用 r *http.Request 这个变量来获取我们希望获得的参数,但是我们经常遇到这样一个场景,我们需要为我们的r设置更多的key-value形式的附加值,一般我们都会存储在一个Map对象中,用的时候再从里面取出,Golang考虑到这样一个场景,为我们提供了一个context,这里的context并不是上下文的意思,属于另外一个这个包github.com/gorilla/context

安装

使用这个库之前,我们同样用Golang中的经典获取包的方式go get命令来获取

代码语言:javascript
复制
go get github.com/gorilla/context

使用方法

设置
代码语言:javascript
复制
context.Set(r, "user", "wuyazi")
context.Set(r, "age", 21)

在上面的这个函数里,r即是*http.Request我们设置了一个key为user,value为wuyazi,另一个key为age,value为21。

获取
代码语言:javascript
复制
user := context.Get(r, "user").(string)
age := context.Get(r, "age").(int)

获取我们使用一个Get()的函数,该函数会返回一个interface{}类型的值,然后我们使用断言,进行类型转换,就能得到我们设置的数据了。

底层原理学习——源码剖析

Set()函数

这个函数接收了3个参数,这3个参数见名知意,这里不再过多介绍,在这个函数里,我们首先可以看到一个关于写入的锁来保证数据的安全性,接着判断我们传入的r是否已经存在了我们的data变量里。这里的data是一个双层嵌套的map,定义如下所示 make(map[*http.Request]map[interface{}]interface{})interface{}类型则表示了我们可以存入各种类型的数据,如果判断data[r]没有值,则为它开辟一个map,如果有值则直接存入,最后释放写入锁。 在此函数中我们同样看到了一个变量datat,datat是用来保存每个r对应map的声明周期,下面会讲到。

代码语言:javascript
复制
func Set(r *http.Request, key, val interface{}) {
    mutex.Lock()
    if data[r] == nil {
        data[r] = make(map[interface{}]interface{})
        datat[r] = time.Now().Unix()
    }
    data[r][key] = val
    mutex.Unlock()
}
Get()函数

同样在这个函数中我们用了一个读的锁,来保证读的安全性,里面的逻辑很简单,基本上和操作map一应,有值返回值,没有值则返回nil。

代码语言:javascript
复制
func Get(r *http.Request, key interface{}) interface{} {
    mutex.RLock()
    if ctx := data[r]; ctx != nil {
        value := ctx[key]
        mutex.RUnlock()
        return value
    }
    mutex.RUnlock()
    return nil
}
GetOk()函数

GetOk函数主要用于判断key是否存在于map对象中,但是为什么会有这个函数,可能有的同学会问,直接判断value是否为nil不就行了,其实这里需要注意的是,nil值同样可以当做value存储在map对象里,因此有这样一个函数是有必要的,这里面函数的逻辑同样和操作map类似,用golang中特有的map对象判断value的方式ok来判断是否有值,如果key不存在将会返回nil,false

代码语言:javascript
复制
func GetOk(r *http.Request, key interface{}) (interface{}, bool) {
    mutex.RLock()
    if _, ok := data[r]; ok {
        value, ok := data[r][key]
        mutex.RUnlock()
        return value, ok
    }
    mutex.RUnlock()
    return nil, false
}
GetAll()函数

这个函数是用来获取所有的键值对,同样用上面的例子使用方法如下:

代码语言:javascript
复制
allParams:=context.GetAll(r)
user:=allParams["user"].(string)
age:=allParams["age"].(int)

首先获得map对象,然后使用断言,比较简单。在源码中,需要注意的是在进行map对象返回的时候,我们新创建了一个和context大小一样的map对象,用来进行map对象的copy,为什么用这样的方法,因为原来的context是一个引用的类型,如果返回引用类型,调用者可能会破坏原来的map结构,因此返回一个map的copy保证的map的安全性。我们在实际开发中同样应该考虑到这一点。这样我们的代码才能更加具有健壮性。

代码语言:javascript
复制
func GetAll(r *http.Request) map[interface{}]interface{} {
    mutex.RLock()
    if context, ok := data[r]; ok {
        result := make(map[interface{}]interface{}, len(context))
        for k, v := range context {
            result[k] = v
        }
        mutex.RUnlock()
        return result
    }
    mutex.RUnlock()
    return nil
}
Delete()和Clear()函数

这两个函数的实现原理和map对象相同,比较简单,不在多阐述,源码如下:

代码语言:javascript
复制
func Delete(r *http.Request, key interface{}) {
    mutex.Lock()
    if data[r] != nil {
        delete(data[r], key)
    }
    mutex.Unlock()
}

func Clear(r *http.Request) {
    mutex.Lock()
    clear(r)
    mutex.Unlock()
}

func clear(r *http.Request) {
    delete(data, r)
    delete(datat, r)
}
Purge()函数

上面我们提到了一个datat的变量,在这个函数中我们就用到了,用它来对我们的key-value进行可控的清理,如果maxAge<0则重新进行创建map,实现清理所有,如果当前时间-maxAge的值大于创建的时间戳,则清理掉数据。

代码语言:javascript
复制
func Purge(maxAge int) int {
    mutex.Lock()
    count := 0
    if maxAge <= 0 {
        count = len(data)
        data = make(map[*http.Request]map[interface{}]interface{})
        datat = make(map[*http.Request]int64)
    } else {
        min := time.Now().Unix() - int64(maxAge)
        for r := range data {
            if datat[r] < min {
                clear(r)
                count++
            }
        }
    }
    mutex.Unlock()
    return count
}
ClearHandler()函数

上面讲到了手动清理,这里则是自动清理,因为在我们进行开发时,随着key-value的增加,我们大多数情况下会忘记清理,因此我们可以使用这个函数。函数的逻辑同样很简单,用Golang提供的defer机制,当这个函数结束之前调用defer延迟,进行清理。

代码语言:javascript
复制
func ClearHandler(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        defer Clear(r)
        h.ServeHTTP(w, r)
    })
}

扩展

在Set()函数中有一个time.Now().Unix()的使用,这个函数是获取当前的时间戳,是time包的一个用法,这里总结了time包对于时间的常用操作,如下:

获取当前时间
代码语言:javascript
复制
currentTime:=time.Now()     //获取当前时间,类型是Go的时间类型Time
t1:=time.Now().Year()        //年
t2:=time.Now().Month()       //月
t3:=time.Now().Day()         //日
t4:=time.Now().Hour()        //小时
t5:=time.Now().Minute()      //分钟
t6:=time.Now().Second()      //秒
t7:=time.Now().Nanosecond()  //纳秒
currentTimeData:=time.Date(t1,t2,t3,t4,t5,t6,t7,time.Local) //获取当前时间,返回当前时间Time     
获取当前时间戳
代码语言:javascript
复制
timeUnix:=time.Now().Unix()            //单位s,打印结果:1491888244
timeUnixNano:=time.Now().UnixNano()  //单位纳秒,打印结果:
获取当前时间的字符串格式
代码语言:javascript
复制
timeStr:=time.Now().Format("2006-01-02 15:04:05") 
时间戳转时间字符串 (int64 —> string)
代码语言:javascript
复制
timeUnix:=time.Now().Unix()   //已知的时间
formatTimeStr:=time.Unix(timeUnix,0).Format("2006-01-02 15:04:05")
时间字符串转时间(string —> Time)
代码语言:javascript
复制
 formatTimeStr="2017-04-11 13:33:37"
 formatTime,err:=time.Parse("2006-01-02 15:04:05",formatTimeStr)
时间字符串转时间戳 (string —> int64)
代码语言:javascript
复制
formatTimeStr="2017-04-11 13:33:37"
formatTime,err:=time.Parse("2006-01-02 15:04:05",formatTimeStr)
formatTime.Unix()

最后

以上的包是在Go1.7引入之前经常使用,在1.7引入后,自带的Context包,即之前讲过的上下文能够很好的进行代替使用方法如下

设置数据

代码语言:javascript
复制
userContext:=context.WithValue(context.Background(),"user","wuyazi")
ageContext:=context.WithValue(userContext,"age",21)
rContext:=r.WithContext(ageContext)

获得数据

代码语言:javascript
复制
user:=r.Context().Value("user").(string)
age:=r.Context().Value("age").(int)

推荐阅读


本文欢迎转载,转载请联系作者,谢谢! 公众号【常更新】:无崖子天下无敌 GitHub:https://github.com/yuwe1 CSDN【看心情更新】: https://blog.csdn.net/weixin_40051278 博客地址【定期更新】:https://yuwe1.github.io/

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-08-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 golang技术杂文 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 导语
  • 安装
  • 使用方法
    • 设置
      • 获取
      • 底层原理学习——源码剖析
        • Set()函数
          • Get()函数
            • GetOk()函数
              • GetAll()函数
                • Delete()和Clear()函数
                  • Purge()函数
                    • ClearHandler()函数
                    • 扩展
                      • 获取当前时间
                        • 获取当前时间戳
                          • 获取当前时间的字符串格式
                            • 时间戳转时间字符串 (int64 —> string)
                              • 时间字符串转时间(string —> Time)
                                • 时间字符串转时间戳 (string —> int64)
                                • 最后
                                • 推荐阅读
                                领券
                                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档