😺
Goのcontextを用いた値の受け渡し
contextとは?
context
は、処理間で状態や値を引き継ぐための仕組みです。GoのWebサーバーでは、1つのリクエストごとにgoroutineが立ち上がりますが、context
を使うことで、「このリクエストに関する情報」を処理全体で安全に共有できます。
Goのcontext
は主に次の3つの目的で使われます。
- リクエストや処理のライフサイクル管理(キャンセルやタイムアウトを制御)
- 値の受け渡し
- 複数処理間で安全に状態を共有する
本記事では特に「値の受け渡し」に焦点を当てて解説します。
contextの特徴
処理を次の処理に渡すことができる。
middlewareからhandlerやusecaseに渡すように、レイヤーを跨いで渡すことができます。また、この時引数を増やすわけではないため、呼び出し側が引数の受け取りを定義する必要がなく、レイヤー間の依存を増やさなくて済みます。
渡す際に値(key-value形式)を付与できる
contextは内部的に、key-value
の値を持っています。keyは独自の型で定義できます。
package contextkey
type userIDKeyType string
const UserIDKey userIDKeyType = "userID"
1リクエストごとに独立しているので、goroutine間でも安全に使える。
もし、グローバル変数を利用していたら、グローバル変数は複数のリクエストで共有されます。同時にリクエストが来た時に値が上書きされるため、バグの原因になります。それに対して、contextはリクエストごとに独立しているため、スレッドセーフな値のやり取りができます。
使い方
処理 | 使い方 |
---|---|
r.Context() | リクエストに紐づいた現在のcontext |
context.WithValue(r.Context(), contextkey.UserIDKey, "user-123") | contextに値を追加して、新しいcontextを返す |
r.WithContext(ctx) | リクエストに新しいcontextをセットし直す |
next.ServeHTTP(w, r.WithContext(ctx)) | 次のHandlerにcontext付きのリクエストを渡す |
r.Context().Value(contextkey.UserIDKey).(string) | 受け取ったcontextをhandlerで取り出す |
Discussion