3. Context
Context は各ハンドラに func(*Context) Node として渡され、リクエスト情報と状態へアクセスするための API を提供します。
リクエストローカル state と共有 state
- そのリクエスト内だけで使う一時値は
Context.Localに置きます。 - アプリ全体で共有する値は
Context.SetGlobal/Context.GetGlobalを使います。名前にGlobalが付く API はアプリの全ユーザーで共有される state を読み書きします。親アプリを持つ context では app mutex 経由で同期されます。
app.Page("/", func(ctx *mb.Context) mf.Node {
ctx.Local["trace_id"] = ctx.Query("trace_id") // リクエスト内だけの一時値
ctx.SetGlobal("last_trace_id", ctx.Local["trace_id"]) // 全ユーザー共有のアプリ state
return mf.Text(ctx.GetGlobal("last_trace_id").(string))
})
Param(name string) string
Request.PathValue(name)からパスパラメータを返します。Requestがnilの場合は""を返します。
Query(name string) string
Request.URL.Query().Get(name)からクエリパラメータを返します。Requestがnilの場合は""を返します。
FormValue(name string) string
Request.FormValue(name)からフォーム値を返します。Requestがnilの場合は""を返します。
Asset(name string) string
- 親アプリからアセット URL を組み立てます。
- 画像、スタイルシート、リンクなどを登録済みアセットプレフィックスに追従させたい場合にハンドラ内で使います。
Local map[string]any
- アプリが作成する context で初期化される、リクエストローカルな一時値用 map です。
- ここに入れた値は他のリクエストと共有されず、app mutex の保護対象でもありません。
SetGlobal(key string, value any)
- アプリ共有 state に書き込みます。
- API 名に
Globalが付くため、この値はアプリの全ユーザー・全リクエストで共有されます。 - app が作成した context では app mutex 経由で同期されます。
- 親アプリがない場合、app-wide state map がないため no-op です。
GetGlobal(key string) any
- アプリ共有 state を読み取ります。
- API 名に
Globalが付くため、この値はアプリの全ユーザー・全リクエストで共有されます。 - app が作成した context では app mutex 経由で同期されます。
- 親アプリがない場合、app-wide state map がないため
nilを返します。 - 返ってきた slice、map、pointer は直接変更せず、変更は
UpdateGlobal内で行うか、GetGlobalSnapshotと clone 関数で snapshot を読んでください。
GetGlobalSnapshot(key string, clone func(any) any) any
- app の read lock を保持したままアプリ共有 state を読み取り、
clone(value)を返します。 - render や後続処理に slice、map などの mutable value を渡す前の snapshot 作成に使います。
- 親アプリがない場合、app-wide state map がないため
clone(nil)を返します。
UpdateGlobal(key string, fn func(old any) any) any
- アプリ共有 state を atomically に読み取り、変換し、書き戻します。
- app が作成した context では、app mutex を取得したまま
old := state[key]、next := fn(old)、state[key] = nextを実行します。 - 新しい値が古い値に依存する場合は、個別の
GetGlobal→SetGlobalではなくこちらを使ってください。 - 親アプリがない場合、app-wide state map がないため
fn(nil)の結果を保存せずに返します。
app.Action("counter/increment", func(ctx *mb.Context) mf.Node {
next := ctx.UpdateGlobal("count", func(old any) any {
oldInt, _ := old.(int)
return oldInt + 1
}).(int)
return mf.Text(strconv.Itoa(next))
})
カウンター、進捗 tick、append 形式の更新では、読み取りと書き込みの間に別リクエストが値を変更する可能性があるため、次の形は避けてください。
count := ctx.GetGlobalInt("count")
ctx.SetGlobal("count", count+1) // UpdateGlobal または IncrementGlobalInt を使う
GetGlobalInt(key string) int
GetGlobal+intアサーションです。- 値がない、または
intでない場合は0を返します。
IncrementGlobalInt(key string, delta int) int
- integer のカウンターや進捗値向けの
UpdateGlobal便利ラッパーです。 - 値がない、または
intでない場合は0として扱い、old + deltaを保存して新しいintを返します。
Flash APIs
Flashes() []FlashMessage
- 現在読み込まれている flash のコピーを返します。
- flash がない場合は
nilを返します。
FlashSuccess(message string) / FlashError(message string) / FlashInfo(message string) / FlashWarn(message string)
AddFlash(level, message)の便利ラッパーです。- level の値は実装定数です:
FlashSuccess,FlashError,FlashInfo,FlashWarn。
AddFlash(level FlashLevel, message string)
- メッセージを trim し、空なら no-op です。
- context の flash リストに追加し、Cookie (
marionette_flash) にシリアライズします。 - Cookie の挙動:
Path=/HttpOnly=trueSameSite=LaxSecureはApp.SetCookieSecureに従います(デフォルトfalse)。
- シリアライズ失敗は無視されます(panic / status 変更なし)。
次リクエストでの lifecycle:
- flash は Cookie から
Context.flashesにデコードされます。 - 既知の level かつ空でないメッセージだけが有効です。
- flash が存在した場合、レスポンスで Cookie は自動的に削除されます。
Session APIs
SetSession(key, value string)
keyを trim し、空なら no-op です。- context メモリ上の session エントリを保存/更新し、Cookie (
marionette_session) に書き込みます。 - Cookie の挙動:
Path=/HttpOnly=trueSameSite=LaxSecureはApp.SetCookieSecureに従います(デフォルトfalse)。
Session(key string) string
- key に対応する session 値を読み取ります。
- key がない場合は空文字列を返します。
ClearSession()
- session map を空の map に置き換え、Cookie (
marionette_session) に書き込みます。
リクエスト時の lifecycle:
- session は
newContextで Cookie からContext.sessionにデコードされます。 - デコード失敗時は空の session map にフォールバックします(panic / status 変更なし)。
Session sample:
app.Page("/session", func(ctx *mb.Context) mf.Node {
user := ctx.Session("user")
if user == "" {
return mf.Form("/session/login", mf.Button("Sign in"))
}
return mf.Form("/session/logout", mf.Button("Sign out"))
})
app.Action("session/login", func(ctx *mb.Context) mf.Node {
ctx.SetSession("user", "Aiko")
return mf.Paragraph("Signed in")
})
app.Action("session/logout", func(ctx *mb.Context) mf.Node {
ctx.ClearSession()
return mf.Paragraph("Signed out")
})
Full example: docs/site-astro/public/examples/go/session.go.