首頁 > 軟體

Go語言讀取,設定Cookie及設定cookie過期方法詳解

2022-04-18 13:00:32

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

$ 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。

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有一個String()方法,用來將Cookie範例轉換成字串。轉化成字串之後就可以直接設定在Header中。

例如,下面是登入youtube的時候,對方傳送給我的cookie:

設定Cookie並行送給使用者端

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, "%sn%sn", 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欄位。

$ 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欄位。

func SetCookie(w ResponseWriter, cookie *Cookie)

注意,第二個欄位是指標型別的Cookie

修改前面的範例,使用SetCookie()函數傳送Set-Cookie欄位:

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資訊。

func (r *Request) Cookie(name string) (*Cookie, error)
func (r *Request) Cookies() []*Cookie
  • Cookie(Name)只取某個cookie
  • Cookies()取所有的cookie

下面是通過Request Header的方式取Cookie的範例:

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, "%sn", 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,將輸出:

first_cookie="Go Programming"; second_cookie="Go Web Programming"

或者,使用curl記錄cookie,並下次存取時讀取cookie:

$ 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:

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, "%sn%sn", cookie, cookies)
}

存取結果:

$ 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,將得到不同的輸出結果。

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就會出現預期的結果:

$ 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

 


IT145.com E-mail:sddin#qq.com