Cookie用来解决http协议无状态的问题。
首先,在服务端生成Cookie,然后在http响应header中设置Set-Cookie字段,客户端会读取到Set-Cookie字段后,会将cookie信息存储起来,下次继续访问服务端时,会在http请求中设置Cookie字段并发送给服务端,服务端可以解析这个Cookie字段,从而知道这个客户端之前已经和自己有过会话(上下文),然后再执行相应的逻辑代码。
Cookie分为两种类型:session cookie和persistent cookie。
- Session Cookie也称为临时Cookie,客户端只会将cookie数据存储在http client进程的内容中,不会保存到磁盘文件中(或其它存储设备),浏览器关闭(或者说http client进程退出)的时候,cookie就删除了。
- persistent cookie是持久化cookie,浏览器退出也不删除,而是根据服务端发送cookie时设置的过期时长判断cookie是否过期,只要cookie还有效,客户端就会携带cookie访问服务端。
Cookie struct
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
$ go doc http.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 Raw string Unparsed [] string // Raw text of unparsed attribute-value pairs } func (c *Cookie) String () string |
一个Cookie代表一个http cookie。服务端可以设置多个Set-Cookie字段发送给客户端。。
Name和Value分别设置这个cookie的key/value。一定要有至少一个能唯一区分客户端的ID类的value。
Expires指定cookie到什么时候过期,是一个时间值。当指定为过去的时间值时,表示这个cookie已经过期。
MaxAge也用来设置cookie什么时候过期,MaxAge为负数或等于0表示立即过期,MaxAge大于0表示过多少秒之后过期。
MaxAge和Expires都可以设置cookie持久化时的过期时长,Expires是老式的过期方法,如果可以,应该使用MaxAge设置过期时间,但有些老版本的浏览器不支持MaxAge。如果要支持所有浏览器,要么使用Expires,要么同时使用MaxAge和Expires。
Path和Domain设置访问哪些路径或域名范围的主机时应该携带这个cookie。如果不设置,则访问所有路径、该Domain下的主机都携带cookie。
1
2
3
4
5
6
|
cookie.Path( "/WEB16" ); 代表访问WEB16应用中的任何资源都携带cookie cookie.Path( "/WEB16/cookietest" ); 代表访问WEB16中的cookietest时才携带cookie信息 cookie.Domain( ".foo.com" ); 这对foo.com域下的所有主机都生效(如www.foo.com),但不包括子域www.abc.foo.com |
Secure和HttpOnly字段为cookie提供一些保护机制。这两个cookie属性的介绍,参见:
- Cookie Secure
- Cookie HttpOnly
Cookie有一个String()方法,用来将Cookie实例转换成字符串。转化成字符串之后就可以直接设置在Header中。
例如,下面是登录youtube的时候,对方发送给我的cookie:
设置Cookie并发送给客户端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
package main import ( "fmt" "net/http" ) func setCookie(w http.ResponseWriter, r *http.Request) { // 定义两个cookie c1 := http.Cookie{ Name: "first_cookie" , Value: "Go Programming" , } c2 := http.Cookie{ Name: "second_cookie" , Value: "Go Web Programming" , HttpOnly: true , } // 设置Set-Cookie字段 w.Header().Set( "Set-Cookie" , c1. String ()) w.Header().Add( "Set-Cookie" , c2. String ()) fmt.Fprintf(w, "%s\n%s\n" , c1. String (), c2. String ()) } func main() { server := http.Server{ Addr: "127.0.0.1:8080" , } http.HandleFunc( "/set_cookie" , setCookie) server.ListenAndServe() } |
访问http://127.0.0.1:8080/set_cookie
时,查看Header将显式Set-Cookie字段。
1
2
3
4
5
6
7
8
9
10
|
$ curl -i http: //127.0.0.1:8080/set_cookie HTTP/ 1.1 200 OK Set-Cookie: first_cookie= "Go Programming" Set-Cookie: second_cookie= "Go Web Programming" ; HttpOnly Date: Tue, 27 Nov 2018 10 : 12 : 44 GMT Content-Length: 75 Content- Type : text/plain; charset=utf- 8 first_cookie= "Go Programming" second_cookie= "Go Web Programming" ; HttpOnly |
http包提供了一个SetCookie()函数,可以直接用来设置Set-Cookie字段。
1
|
func SetCookie(w ResponseWriter, cookie *Cookie) |
注意,第二个字段是指针类型的Cookie。
修改前面的示例,使用SetCookie()函数发送Set-Cookie字段:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
func setCookie(w http.ResponseWriter, r *http.Request) { c1 := http.Cookie{ Name: "first_cookie" , Value: "Go Programming" , } c2 := http.Cookie{ Name: "second_cookie" , Value: "Go Web Programming" , HttpOnly: true , } http.SetCookie(w, &c1) http.SetCookie(w, &c2) } |
取得客户端携带的cookie
由于客户端发起请求时,如果携带cookie,是直接放在Request的Cookie Header中的。所以,可以通过Request取得客户端携带的cookie信息。当然,也可以通过Request的方法Cookie()或Cookies()取得cookie信息。
1
2
|
func (r *Request) Cookie(name string ) (*Cookie, error ) func (r *Request) Cookies() []*Cookie |
- Cookie(Name)只取某个cookie
- Cookies()取所有的cookie
下面是通过Request Header的方式取Cookie的示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
package main import ( "fmt" "net/http" ) func setCookie(w http.ResponseWriter, r *http.Request) { c1 := http.Cookie{ Name: "first_cookie" , Value: "Go Programming" , } c2 := http.Cookie{ Name: "second_cookie" , Value: "Go Web Programming" , HttpOnly: true , } http.SetCookie(w, &c1) http.SetCookie(w, &c2) } func getCookie(w http.ResponseWriter, r *http.Request) { cookie := r.Header.Get( "Cookie" ) fmt.Fprintf(w, "%s\n" , cookie) } func main() { server := http.Server{ Addr: "127.0.0.1:8080" , } http.HandleFunc( "/set_cookie" , setCookie) http.HandleFunc( "/get_cookie" , getCookie) server.ListenAndServe() } |
在访问http://127.0.0.1:8080/set_cookie
之后不要关闭浏览器,再次访问http://127.0.0.1:8080/get_cookie
,将输出:
1
|
first_cookie= "Go Programming" ; second_cookie= "Go Web Programming" |
或者,使用curl记录cookie,并下次访问时读取cookie:
1
2
3
|
$ curl -c a.cookie http: //127 .0.0.1:8080 /set_cookie $ curl -b a.cookie http: //127 .0.0.1:8080 /get_cookie first_cookie= "Go Programming" ; second_cookie= "Go Web Programming" |
下面是改用Request的Cookie()和Cookies()方法取cookie:
1
2
3
4
5
6
7
8
|
func getCookie(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie( "first_cookie" ) if err != nil { fmt.Fprintf(w, "Cat't get Cookie" ) } cookies := r.Cookies() fmt.Fprintf(w, "%s\n%s\n" , cookie, cookies) } |
访问结果:
1
2
3
4
|
$ curl -c a.cookie http: //127 .0.0.1:8080 /set_cookie $ curl -b a.cookie http: //127 .0.0.1:8080 /get_cookie first_cookie= "Go Programming" [first_cookie= "Go Programming" second_cookie= "Go Web Programming" ] |
设置cookie过期示例:发送临时消息
有时候可能想要让客户端的某些操作只显示一次相关消息,例如post一篇帖子失败后,应该显示失败信息,但下次再访问不应该再显示这些失败信息。
通过设置cookie过期的技巧,可以实现一些一次性操作。设置cookie过期的方式是设置MaxAge为负数或0,为了兼容所有浏览器,可以设置Expires为过去的一段时间。
下面的示例中,将一段数据使用URL格式编码后作为flash cookie的值。当客户端访问set_message的时候,就会在http Client进程中保存这段cookie。再访问show_message的时候,handler解析客户端携带的cookie,并设置一个Set-Cookie字段,这个字段的作用是使之前保存的cookie过期。然后输出解码后客户端携带的cookie的值。再次刷新show_message,将得到不同的输出结果。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
package main import ( "encoding/base64" "fmt" "net/http" "time" ) func set_message(w http.ResponseWriter, r *http.Request) { msg := [] byte ( "Hello World" ) cookie := http.Cookie{ Name: "flash" , Value: base64.URLEncoding.EncodeToString(msg), } http.SetCookie(w, &cookie) } func show_message(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie( "flash" ) if err != nil { if err == http.ErrNoCookie { fmt.Fprintln(w, "no messages to show" ) } } else { expire_cookie := http.Cookie{ Name: "flash" , MaxAge: - 1 , Expires: time.Unix( 1 , 0 ), } http.SetCookie(w, &expire_cookie) value, _ := base64.URLEncoding.DecodeString(cookie.Value) fmt.Fprintln(w, string (value)) } } func main() { server := http.Server{ Addr: "127.0.0.1:8080" , } http.HandleFunc( "/set_message" , set_message) http.HandleFunc( "/show_message" , show_message) server.ListenAndServe() } |
使用curl测试。注意,首先访问set_message的时候,保存cookie到b.cookie文件。再访问show_message的时候,也要带上-c b.cookie
将已保存的cookie设置为过期,之后再访问show_message就会出现预期的结果:
1
2
3
4
5
6
|
$ curl -c b.cookie http: //127 .0.0.1:8080 /set_message $ curl -b b.cookie -c b.cookie http: //127 .0.0.1:8080 /show_message Hello World $ curl -b b.cookie -c b.cookie http: //127 .0.0.1:8080 /show_message no messages to show |
原文链接:https://www.cnblogs.com/f-ck-need-u/p/10035803.html