Golang Cookie Note

Rain Wu
Golang 筆記
Published in
5 min readApr 17, 2019

在期中前寫筆記,在逆境中求生存!儘管學校的東西不斷壓過來,仍堅持以筆記反思並檢視學習成效。

Cookie 是什麼

若先從一個最為陽春的網路溝通方式來看,當一個 Client 端和一個 Server 端發送請求,並由 Server 端處理後進行響應,這一整個過程會因為 http 的無狀態特性,不著痕跡也不留紀錄。問題在於,多數時候留些有用的東西比較好。

假設今天我們透過帳號密碼登入,進到了某個平台服務的會員個人頁面,而這些會員頁面如果沒有登入是無法訪問到的。換句話說,如果在過程中什麼都沒存的話,勢必每一次訪問不同的會員頁面,都需要輸入一次帳號密碼來驗證身分,那想必十分破壞用戶體驗。

Cookie 的概念正是為了要解決這問題而誕生的,在 Client 端第一次發送請求至 Server 端時,Server 端在處理過程中便生成一個標示,也就是 Cookie。這 Cookie 中存了些需要的資料,並且包裝好連同響應一起回覆給 Client 端,讓他在下次發送請求時,可以不用重複麻煩的輸入流程,就可以發送帶有必備資料的請求。

而這些存在於 Client 端的 Cookie 也是會有期限的,若無特別設置的話,Cookie 會在 Client 端瀏覽器被關閉的時刻慘遭清除。若特別去設定,則可以使其持久化。

SetCookie

如果要在使用者的 Browser 中置入 Cookie,在 Golang 中可以這樣寫:

為方便在 Browser 操作所以把它包成了個 func(http.ResponseWriter, *http.Request) , line 5~6 中設定了 expiration 的時間限制,範例中是給了一年,這部分會用到 time 的 package。line 9 的部分生成了一個帶有特定資料的 cookie,然後在 line 12 設定。先來看看 cookie 中的樣子:

http.Cookie 中其實有蠻多可以設定的東西,範例只針對 Name, Value, 和 Expire 設定,其餘皆使用預設值。再生成完這個 Cookie 後,究竟是如何把它傳遞到 user 端儲存的呢?這就來看看 http.SetCookie 中是怎麼做的:

可以看到他是透過 http.ResponseWriter 一併在回應時帶上的資訊,http.ResponseWriter 是一個定義 HTTP 響應的 interface,其中有個 Header() 方法會回傳下面的 Header 結構,也就是 HTTP 中溝通時置入附加資訊的頭欄位,可以使用他的 Add 方法來帶上 Cookie 資訊,而 Header 內部是長這樣子的:

Header 的主體是個 map 結構,key 是 string 型別,而 value 是 []string,而他的 Add 方法便是需要 key 和 value 兩個參數,內部則是用到了另一個 package 也就是 "net/textproto" 的功能,以原有 header 來構建一個新的 MIMEHeader 用來置入資料。

MIME 為 multipurpose Internet mail extensions 的意思,是一種表達文件類型的標準格式,因為 Browser 通常不會使用文件的附檔名來判別其類型。因此 MIME 主要用於支援純字串傳遞以外,也能傳遞多種檔案類型的功能,更詳細的內容可以參考 這份文件 ~

同樣來看看 textproto.MIMEHeader 內部的實現方式:

MIMEHeader 同樣也是 map[string][]string 的結構,line 9 的部分很直觀的運用 append 新增 key-value pair,但 line 8 的 CanonicalMIMEHeaderKey 究竟是什麼呢?

其實她只是個把 key 的字串給規範化的工具,但這非常重要,因為一旦傳入 go http server 後就會變得 case-sensitive,因此需要有正確的規範才能正確操作。可以發現 Header.Add() 的內部偷偷埋了這個指令,因此即使單純使用 Header.Add() 而不特別先進行 key 字串的規範化也不會因為大小寫問題而出錯,是十分有效的防呆機制呢 XD

GetCookie

Cookie 是個儲存在 Browser 的資料,再發起請求時會帶上它一併傳至 Server,那麼在 Golang HTTP Server 中便可以取得 Cookie 的值。

主要都是對 http.Request 中取出 Cookies,前者可特別指定 name 參數取出單一 Cookie,若找不到的話便會跳出 Error,因此需要特別處理,後者則是透過迴圈取出所有 Cookies:

兩個種都有用到一個關鍵的方法,就是 readCookies,從原始碼來看絕大多數功能也是在這方法中實現,它同樣需要一個 http.Request.Header 作為溝通時傳遞資料的載體,和上述 http.ResponseWriter.Header 的機制類似,就不再贅述了,直接來關心一下 readCookies 怎麼做的:

readCookies 主要做的就是從 Header 中提取 Cookie 的部分並逐一比對字串,如果是需要的就留下,不需要的就略過,中間也做了完善的異常處理,包含 parseCookieValue 把頭尾的 Double Quote 去除以及 Cookie 中字元的合法性檢查:

最後把需要的 Cookie 一併以 *[]Cookie 回傳,如此我們便能對回傳的 Cookie 結構取值。

這次就先寫到這兒,不過光是 Cookie 就寫了不小的篇幅,幾天後寫 Session 的筆記應該也要費不少心力,歡迎各位路過的 Golang 大大給些指教~

--

--

Rain Wu
Golang 筆記

A software engineer specializing in distributed systems and cloud services, desire to realize various imaginations of future life through technology.