Go Web (Part 4): Cookie

HTTP是一个无状态的协议。

这意味着服务器不会记录客户的状态。但是有时候服务器需要知道客户端的状态,这个时候就可以使用Cookie了。

在Go中,定义了Cookie的结构:

type Cookie struct {
    Name  string
    Value string

    Path       string    // optional
    Domain     string    // optional
    Expires    time.Time // optional
    RawExpires string    // for reading cookies only

    // MaxAge=0 means no 'Max-Age' attribute specified.
    // MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
    // MaxAge>0 means Max-Age attribute present and given in seconds
    MaxAge   int
    Secure   bool
    HttpOnly bool
    SameSite SameSite
    Raw      string
    Unparsed []string // Raw text of unparsed attribute-value pairs
}

其实Cookie就是一个key-value对,用来存储一些简单的信息。也就是上面的字段中,NameValue是必须的,其余的都是可选的。

ExpiresMaxAge都可以用来设置Cookie的有效期。不同在于:

  1. Expires指定了Cookie过期的具体时间,比如一个Cookie会在2020-02-02 02:02:02过期;
  2. MaxAge指定了一个Cookie在被浏览器创建后可以存在的时间,单位是秒;
  3. 几乎所有的浏览器都支持Expires,但是IE6、IE7和IE8不支持MaxAge

使用这两种方式不是Go的原因,而是浏览器的支持情况决定的。

2. 设置Cookie

服务器设置Cookie本质上就是将一个字符串写入response中的”Set-Cookie” Header中。而这个写入的字符串,是对Cookie结构的一个序列化结果(就是Cookie.String()方法)。

c := http.Cookie{
    Name:  "cookie",
    Value: "go web",
}
w.Header().Set("Set-Cookie", c.String())

打开浏览器可以看到下面的结果:

也可以使用http.SetCookie()方法:

c := http.Cookie{
    Name:  "cookie",
    Value: "go web",
}
http.SetCookie(w, &c)

结果也是一样,不过要注意,这里使用的是Cookie的指针。

3. 读取Cookie

服务器写入Cookie的Header是Set-Cookie,浏览器向服务器传递Cookie的Header是Cookie,可以通过这个Header来读取浏览器的Cookie:

h := r.Header["Cookie"]

这样获取到的是所有的Cookie切片。为了获取所有的Cookie,还可以使用下面的方法:

// Cookies parses and returns the HTTP cookies sent with the request.
func (r *Request) Cookies() []*Cookie {
    return readCookies(r.Header, "")
}

上面两个方法获得的是所有的Cookie,还可以获取指定的Cookie:

// Cookie returns the named cookie provided in the request or
// ErrNoCookie if not found.
// If multiple cookies match the given name, only one cookie will
// be returned.
func (r *Request) Cookie(name string) (*Cookie, error) {
    for _, c := range readCookies(r.Header, name) {
        return c, nil
    }
    return nil, ErrNoCookie
}

不过这会在Cookie不存在的时候返回一个错误。具体使用如下:

c, err := r.Cookie("cookie")
if err != nil {
    fmt.Println("no such cookie")
}

4. 删除一个Cookie

Go里没有删除Cookie的方法,不过可以通过设置一个Cookie有效期来间接删除一个Cookie。

可以将一个Cookie的Expires设置为一个过去的时间,或者MaxAge设置为负数,就可以删除一个Cookie了:

c := http.Cookie{
    Name: "old_cookie",
    MaxAge: -1,
    Expires: time.Unix(1, 0),
}
http.SetCookie(w, &c)

这篇文章简单介绍了一下在Go中如果使用Cookie,包括添加、读取以及删除。

To Be Continued…


 Previous
SSL and TLS (Part 1): Brief Intro SSL and TLS (Part 1): Brief Intro
在万物互联的时代,We don’t connect to the Internet, we are the Internet. 如此重要的互联网,需要一种协议来保护传输中的信息安全,那就是SSL(Secure Socket Layer)和T
2020-01-15
Next 
Go Web (Part 3): response Go Web (Part 3): response
有请求就要有响应,一来一回。这篇文章来看看response的细节。 1. 从处理器Handler开始对于Request,在前一篇文章中已经知道了整个的流程。Go会构造一个Request结构体,将接收到的请求中的信息解析到这个结构体中。对
2020-01-02
  You Will See...