はじめに
前回、レスポンスについてまとめたのですが、長くなるためクッキーは割愛しました。
そこで今回はサーバーからHTTPレスポンスメッセージを通して送られるクッキー
についてまとめます。
クッキー
クッキーは、サーバーがクライアントに送信し、クライアント側で保存される情報の小さな断片です。例えば、ブラウザ(Chrome)で検証すれば簡単に確認できます。(下の画像は何も入っていません。)
サーバーはステートレスであるため、一度クライアントとのやり取りが終わるとその状態を保持しません。そのため、次のリクエストが来たときにそのクライアントが誰なのかを覚えていません。
例えば、ログインしたユーザーが誰であるかを認識するためには、セッション情報をクッキーに保存し、そのクッキーをリクエストのたびに送信することで、サーバーがユーザーを認識することができます。
余談ですが、最近はプライバシー保護の観点からサードパーティクッキー廃止の流れとなっています。最近よくクッキーを許可するかどうか確認されますよね。(個人的に面倒なのでやめてほしい。)
これに関して、疑問なのですが、フロントとバックエンドでAPI通信する場合、ドメインが異なる時はクッキーを使えなくなるんでしょうか。分かる方誰か教えてください。
Goに話を移します
ちょっと前置きが長くなりましたが、cookie
の扱いをGo言語で確認します。
以下がCookie
構造体です。まあ、一般的な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
}
一点補足すると、MaxAge
とExpiers
は秒数か日時かの違いなどありますが、期限を決めるという意味では同じものです。ですので、どちらも設定することが可能ですが基本的にMaxAge
が優先されます。
Cookie
構造体を使用してクッキーをブラウザに登録してみる
ブラウザにクッキーを登録するためには、HTTPレスポンスヘッダーにSet-Cookieヘッダー
を含めます。ブラウザはこのヘッダーを受け取ると、指定されたクッキーを保存します。
したがって、以下のようにCookie
構造体に必要な値を設定し、Set-Cookieをキー
、c.String()を値
として、ヘッダーに追加します。
こうすると、レスポンスが返ってくるとブラウザのクッキーにセットされます。
Cookie構造体にはString()
がありますが、これは__Cookie構造体をHTTPヘッダー用の文字列に変換し、それを返却してくれます。__
package main
import "net/http"
func CookieSample(w http.ResponseWriter, r *http.Request){
c := http.Cookie{
Name: "name",
Value: "value",
HttpOnly: true,
}
w.Header().Set("Set-Cookie", c.String())
}
func main(){
server := http.Server{
Addr: "127.0.0.1:8080",
}
http.HandleFunc("/cookie", CookieSample)
server.ListenAndServe()
}
試しにhttp://127.0.0.1:8080/cookie
へブラウザでリクエストすると、以下のようにセットされます。
実はSetCookieメソッドがある
ヘッダーにSet-Cookie
でクッキーをセットするのでも十分簡単なのですが、実はhttp.SetCookie
を使えばより簡潔に書けるみたいです。
先ほどのコードを以下のように変えるだけです。こちらの方が明示的で分かりやすいですね。参照渡しに変わっている点だけ注意が必要です。
http.SetCookie(w, &c)
// w.Header().Set("Set-Cookie", c.String())
SetCookie
の定義は以下の通りですが、やっていることは同じですね。
func SetCookie(w ResponseWriter, cookie *Cookie) {
if v := cookie.String(); v != "" {
w.Header().Add("Set-Cookie", v)
}
}
クッキーの取得
では、セットしたクッキーを次はサーバー側でどのように取得するのでしょうか。
あまり複雑ではないので簡潔に書きます。
まず、最初に思いつきそうなのが以下のようにHeaderからキーを指定して取得する方法ですが、これだと文字列のスライスが返ってくるため、自分で解析して必要な名前と値のペアを取得する必要があります。
func GetCookie(w http.ResponseWriter, r *http.Request){
c := r.Header["cookie"]
}
ですので、これを避けるためにCookie
メソッドを使用できます。これは引数でクッキーの名前を指定すると、ヘッダーの中からその名前に一致する値を探して、返却します。
func GetCookie(w http.ResponseWriter, r *http.Request){
c, err := r.Cookie("first_cookie")
}
ちなみにCookie()
だと単一のクッキーですが、複数のクッキーを取得したい場合にはCookies()
が用意されています。