Zenn
😺

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

ログインするとコメントできます