gin.Contextとgin.Context.Request.Context()はどちらを使えば良い?
注:この記事はginのv1.9.0とそれ以前のバージョンを対象としています。
gin.Contextはcontext.Contextインターフェースを実装しています。
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key any) any
}
ですがgin.Contextはhttp.Request構造体を型にとるRequestフィールドを持っていて、http.Requestにもcontext.Contextを実装するctxフィールドを持っています。(http.Request.ctxはhttp.Request.Context()を使えば取得できます)
この2つのContextはどう違うのでしょうか?
とりあえずどちらを使えばいいのでしょうか?
状況によって使い分けるのでしょうか?
ソースコードを見ていきます。
Deadline()、Done()、Err()は直前にif文に当てはまる場合はゼロ値を返しますが基本的にhttp.Request.Context()と同じものを返しています。
func (c *Context) Deadline() (deadline time.Time, ok bool) {
if !c.engine.ContextWithFallback || c.Request == nil || c.Request.Context() == nil {
return
}
return c.Request.Context().Deadline()
}
func (c *Context) Done() <-chan struct{} {
if !c.engine.ContextWithFallback || c.Request == nil || c.Request.Context() == nil {
return nil
}
return c.Request.Context().Done()
}
func (c *Context) Err() error {
if !c.engine.ContextWithFallback || c.Request == nil || c.Request.Context() == nil {
return nil
}
return c.Request.Context().Err()
}
問題はValue()です。
Value()が返す値は5通りあるようです。
- 引数が0のときはc.Requestそのものを返す
- "_gin-gonic/gin/contextkey"という文字列のときは*gin.Context自体を返す
- その他の文字列のときはc.Keysに一致するキーがあればそれを返す
- Context.Request.Context()かc.Requestがnilの場合はnilを返す
- 1〜4までの条件に当てはまらなかったか3で一致するキーが無かった場合はc.Request.Context().Value()をの戻り値を返す
func (c *Context) Value(key any) any {
if key == 0 {
return c.Request
}
if key == ContextKey {
return c
}
if keyAsString, ok := key.(string); ok {
if val, exists := c.Get(keyAsString); exists {
return val
}
}
if !c.engine.ContextWithFallback || c.Request == nil || c.Request.Context() == nil {
return nil
}
return c.Request.Context().Value(key)
}
1〜3の戻り値はすべて他のメソッドやフィールドから直接取得できるため、わざわざ設けている理由はわかりませんでした。
もしかしたら将来的にValue()からのみ取得できるようにして行くのかもしれません。(要確認
v1.8.0以前
gin.Contextの各メソッドが上記のような値を返すようになったのはv1.8.0からであり、それ以前もcontext.Contextを実装してはいたものの、Deadline()、Done()、Err()はnilしか返さず、コメントではc.Request.Context()の使用を推奨していました。Value()は上で書いた戻り値の1と3とそれらに当てはまらなかった場合はnilを返していて、c.Request.Context()の値は全く使用されていませんでした。
結論
調査が足りていないですがv1.8.0以降を導入する場合はgin.Context、それ以前のバージョンでcontext.Contextを使う場合はgin.Context.Requestを使うのが良さそうです。
株式会社SODAの開発組織がお届けするZenn Publicationです。 是非Entrance Bookもご覧ください! → recruit.soda-inc.jp/engineer
Discussion