実行コンテキスト(ExecutionContext)
Spineリクエストの中核。
概要
ExecutionContextは、Spineパイプライン全体で共有される要求スコープのコンテキストです。 HTTP要求が到着すると、TransportアダプターはExecutionContextを生成し、このコンテキストはパイプラインのすべてのステップを通過し、要求情報と実行状況を渡します。
Context階層
SpineはContextを階層的に分離します。これは、HTTP、Event Consumer、WebSocketを同じパイプラインモデルとして処理するための設計です。
なぜそう分けるか。
|階層担当|使用場所| |------|------|----------| | ContextCarrier | Go標準コンテキスト配信|どこでも | EventBusCarrier |ドメインイベント発行(core.EventBus)|コントローラー、コンシューマー | ExecutionContext |実行フロー制御|ルーター、パイプライン、インターセプター| | ControllerContext | ExecutionContextの読み取り専用Facade |コントローラー(インターセプター注入値を参照) | HttpRequestContext | HTTP入力の解釈HTTP ArgumentResolver | | ConsumerRequestContext |イベント入力の解釈Consumer ArgumentResolver | | WebSocketContext | WebSocket入力解析| WebSocket ArgumentResolver |
目標:HTTP、Event Consumer、WebSocketが同じパイプラインモデルを共有しながら、各プロトコルの特性に合った入力解析が可能になります。
ベースのインターフェース
ContextCarrier
Go 標準 context.Context を渡す最小契約です。
// core/context.go
type ContextCarrier interface {
Context() context.Context
}EventBusCarrier
ドメインイベントを発行するためのEventBusアクセス契約です。戻り型はcore.EventBusです。
// core/context.go
type EventBusCarrier interface {
EventBus() EventBus
}core.EventBusは、ドメインイベントを収集して実行後に一度に発行するための最小契約です。
// core/event_bus.go
type EventBus interface {
Publish(events ...publish.DomainEvent)
Drain() []publish.DomainEvent
}注:
internal/event/publish.EventBusはcore.EventBusのタイプ別名 (type EventBus = core.EventBus) で、内部実装がこのタイプを満たすように構成されます。
ExecutionContextインタフェース
パイプライン全体で使用される実行フロー制御用のインタフェースです。
// core/context.go
type ExecutionContext interface {
ContextCarrier
EventBusCarrier
// HTTP リクエスト 情報 (Consumer/WebSocketでは 意味が 異なる)
Method() string // HTTP: GET, POST... / Consumer: "EVENT" / WS: "WS"
Path() string // HTTP: /users/123 / Consumer: EventName / WS: path
Header(name string) string // HTTP ヘッダー (Consumer, WSは 空文字列)
// パラメータ アクセス
Params() map[string]string // Path parameters
PathKeys() []string // Path key 順序
Queries() map[string][]string // Query parameters
// 内部 保存所
Set(key string, value any) // 値 保存
Get(key string) (any, bool) // 値 参照
}メソッドの詳細
Context()
Go 標準 context.Context を返します。要求のキャンセル、タイムアウト、値の転送に使用されます。
func (e *echoContext) Context() context.Context {
return e.reqCtx // HTTP リクエストの context
}EventBus()
リクエストスコープのEventBusを返します。 Controllerがドメインイベントを発行するときに使用されます。
func (c *echoContext) EventBus() publish.EventBus {
return c.eventBus
}Method() / Path()
HTTP リクエストのメソッドとパスを返します。 Consumer と WebSocket では異なる意味で使用されます。
// HTTP
ctx.Method() // "GET"
ctx.Path() // "/users/123/posts/456"
// Consumer
ctx.Method() // "EVENT"
ctx.Path() // "order.created" (EventName)
// WebSocket
ctx.Method() // "WS"
ctx.Path() // WebSocket パスParams() / PathKeys()
Path パラメータ情報を提供します。
// Route: /users/:userId/posts/:postId
// Request: /users/123/posts/456
ctx.Params() // {"userId": "123", "postId": "456"}
ctx.PathKeys() // ["userId", "postId"]PathKeys()は、パラメータの宣言順序を保証します。 Spineの順序ベースのバインディングに不可欠です。
Queries()
クエリパラメータを複数値の形式で返します。
// Request: /users?status=active&tag=go&tag=web
ctx.Queries() // {"status": ["active"], "tag": ["go", "web"]}Set() / Get()
パイプライン内で値を共有するリポジトリ。
// Routerで path params 保存
ctx.Set("spine.params", params)
ctx.Set("spine.pathKeys", keys)
// Adapterで ResponseWriter 保存
ctx.Set("spine.response_writer", NewEchoResponseWriter(c))
// Interceptorで 参照
rw, ok := ctx.Get("spine.response_writer")ControllerContext インタフェース
Controller専用のContext Viewです。 ExecutionContextの読み取り専用Facadeで、Interceptorから注入した値をControllerで参照するための公式通路です。
// core/context.go
type ControllerContext interface {
Get(key string) (any, bool)
}実装
// internal/runtime/controller_ctx.go
type controllerCtxView struct {
ec core.ExecutionContext
}
func NewControllerContext(ec core.ExecutionContext) core.ControllerContext {
return controllerCtxView{ec: ec}
}
func (v controllerCtxView) Get(key string) (any, bool) {
return v.ec.Get(key)
}使用例
// Controllerで Interceptorが 注入した 値 参照
func (c *UserController) GetUser(ctx context.Context, cc core.ControllerContext, userId path.Int) User {
authInfo, _ := cc.Get("auth.user")
// ...
}注意:
pkg/spine/types.goにはCtxインタフェース(Get(key string) (any, bool))が定義されており、ユーザーコードからspine.Ctxにもアクセス可能です。
HttpRequestContext インタフェース
HTTP 専用拡張インターフェイスです。 HTTP ArgumentResolverで使用されます。
// core/context.go
type HttpRequestContext interface {
ContextCarrier
EventBusCarrier
// 個別 パラメータ アクセス
Param(name string) string // 特定の path param
Query(name string) string // 特定の query param (最初の 値)
Header(name string) string // 特定の ヘッダー
// 全体 ビュー アクセス
Params() map[string]string // すべて path params
Queries() map[string][]string // すべて query params
Headers() map[string][]string // すべて ヘッダー
// Body バインディング
Bind(out any) error // JSON body → struct
// Multipart
MultipartForm() (*multipart.Form, error)
}注:
HttpRequestContextにはRequestContextは含まれていません。ContextCarrierとEventBusCarrierを直接埋め込みます。さらに、Headers() map[string][]stringメソッドが追加され、ヘッダーマップ全体にアクセスできます。
メソッドの詳細
Param() / Query()
個々のパラメータに便利にアクセスします。
// Route: /users/:id?page=1&size=20
ctx.Param("id") // "123"
ctx.Query("page") // "1"
ctx.Query("size") // "20"
ctx.Query("missing") // "" (なければ 空文字列)Bind()
HTTP bodyを構造体にバインドします。
// internal/resolver/dto_resolver.go
func (r *DTOResolver) Resolve(ctx core.ExecutionContext, parameterMeta ParameterMeta) (any, error) {
httpCtx, ok := ctx.(core.HttpRequestContext)
if !ok {
return nil, fmt.Errorf("HTTP リクエスト コンテキストが ではありません")
}
valuePtr := reflect.New(parameterMeta.Type)
if err := httpCtx.Bind(valuePtr.Interface()); err != nil {
return nil, fmt.Errorf("DTO バインディング 失敗 (%s): %w", parameterMeta.Type.Name(), err)
}
return valuePtr.Elem().Interface(), nil
}MultipartForm()
Multipart form データにアクセスします。ファイルアップロード処理に使用されます。
// internal/resolver/uploaded_files_resolver.go
func (r *UploadedFilesResolver) Resolve(ctx core.ExecutionContext, parameterMeta ParameterMeta) (any, error) {
httpCtx, ok := ctx.(core.HttpRequestContext)
if !ok {
return nil, fmt.Errorf("HTTP リクエスト コンテキストが ではありません")
}
form, err := httpCtx.MultipartForm()
if err != nil {
return nil, err
}
// ...
}ConsumerRequestContext インタフェース
Event Consumer 専用の拡張インタフェースです。
// core/context.go
type ConsumerRequestContext interface {
ContextCarrier
EventBusCarrier
EventName() string // イベント 名前 (例: "order.created")
Payload() []byte // イベント ペイロード (JSON など)
}メソッドの詳細
EventName()
受信したイベントの名前を返します。
ctx.EventName() // "order.created"Payload()
イベントの生のペイロードを返します。
payload := ctx.Payload() // []byte (JSON)Consumer Resolverの例
// internal/event/consumer/resolver/dto_resolver.go
func (r *DTOResolver) Resolve(ctx core.ExecutionContext, meta resolver.ParameterMeta) (any, error) {
consumerCtx, ok := ctx.(core.ConsumerRequestContext)
if !ok {
return nil, fmt.Errorf("ConsumerRequestContextが ではありません")
}
payload := consumerCtx.Payload()
if payload == nil {
return nil, fmt.Errorf("Payloadが 空のため DTOを 生成する ことが ありません")
}
dtoPtr := reflect.New(meta.Type)
if err := json.Unmarshal(payload, dtoPtr.Interface()); err != nil {
return nil, fmt.Errorf("DTO デシリアライズに 失敗しました: %w", err)
}
return dtoPtr.Elem().Interface(), nil
}WebSocketContext インタフェース
WebSocket 専用の ExecutionContext 拡張です。 ExecutionContextを埋め込み、パイプラインの互換性を維持します。
// core/context.go
type WebSocketContext interface {
ExecutionContext
ConnID() string // 接続 ID
MessageType() int // メッセージ 型 (Text, Binary など)
Payload() []byte // メッセージ ペイロード
}WebSocket Resolverの例
// internal/ws/resolver/dto_resolver.go
func (r *DTOResolver) Resolve(ctx core.ExecutionContext, meta resolver.ParameterMeta) (any, error) {
wsCtx, ok := ctx.(core.WebSocketContext)
if !ok {
return nil, fmt.Errorf("WebSocketContextが ではありません")
}
payload := wsCtx.Payload()
if payload == nil {
return nil, fmt.Errorf("Payloadが 空のため DTOを 生成する ことが ありません")
}
dtoPtr := reflect.New(meta.Type)
if err := json.Unmarshal(payload, dtoPtr.Interface()); err != nil {
return nil, fmt.Errorf("DTO デシリアライズ 失敗: %w", err)
}
return dtoPtr.Elem().Interface(), nil
}Echoアダプタの実装
Spine は Echo を HTTP Transport レイヤーとして使用します。 echoContextはExecutionContextとHttpRequestContextの両方を実装します。
// internal/adapter/echo/context_impl.go
type echoContext struct {
echo echo.Context // Echoの 元の コンテキスト
reqCtx context.Context // リクエスト スコープ コンテキスト
store map[string]any // 内部 保存所
eventBus publish.EventBus // イベント バス
}
func NewContext(c echo.Context) core.ExecutionContext {
return &echoContext{
echo: c,
reqCtx: c.Request().Context(),
store: make(map[string]any),
eventBus: publish.NewEventBus(),
}
}主な実装
Path Parameters
Routerがマッチングした結果を優先し、なければEchoの値を使用します。
func (e *echoContext) Param(name string) string {
// Spine Routerが 保存した 値 優先
if raw, ok := e.store["spine.params"]; ok {
if m, ok := raw.(map[string]string); ok {
if v, ok := m[name]; ok {
return v
}
}
}
// Fallback to Echo
return e.echo.Param(name)
}Params() - 防御コピー
外部からソースマップを変更しないようにコピーを返します。 maps.Copyを使用してください。
func (e *echoContext) Params() map[string]string {
if raw, ok := e.store["spine.params"]; ok {
if m, ok := raw.(map[string]string); ok {
// return a shallow copy to avoid mutation
copyMap := make(map[string]string, len(m))
maps.Copy(copyMap, m)
return copyMap
}
}
// Echoで 直接 構成
names := e.echo.ParamNames()
values := e.echo.ParamValues()
params := make(map[string]string, len(names))
for i, name := range names {
if i < len(values) {
params[name] = values[i]
}
}
return params
}Headers()
すべてのHTTPヘッダーをマップとして返します。
func (e *echoContext) Headers() map[string][]string {
return e.echo.Request().Header
}EventBus
リクエストスコープのEventBusを返します。
func (c *echoContext) EventBus() publish.EventBus {
return c.eventBus
}Consumerアダプタの実装
Event Consumer 用の Context 実装です。
// internal/event/consumer/request_context_impl.go
type ConsumerRequestContextImpl struct {
ctx context.Context
msg *Message
eventBus publish.EventBus
store map[string]any
}
func NewRequestContext(
ctx context.Context,
msg *Message,
eventBus publish.EventBus,
) core.ExecutionContext {
return &ConsumerRequestContextImpl{
ctx: ctx,
msg: msg,
eventBus: eventBus,
store: make(map[string]any),
}
}Consumer Contextの特殊動作
Consumer は HTTP ではないため、いくつかのメソッドが異なる動作をします。
func (c *ConsumerRequestContextImpl) Method() string {
// Consumer 実行は HTTP Methodの概念がなく, ルーティング 区切りを ため "EVENT" 使用
return "EVENT"
}
func (c *ConsumerRequestContextImpl) Path() string {
// Consumer ルーティングで Pathは EventNameを そのままに 使用
return c.msg.EventName
}
func (c *ConsumerRequestContextImpl) Header(key string) string {
// Consumerには HTTP Header 概念は なし
return ""
}
func (c *ConsumerRequestContextImpl) Params() map[string]string {
// Consumerには Path Parameter 概念は なし
return map[string]string{}
}
func (c *ConsumerRequestContextImpl) PathKeys() []string {
// Consumerには Path Key 概念は なし
return []string{}
}
func (c *ConsumerRequestContextImpl) Queries() map[string][]string {
// Consumerには Query Parameter 概念は なし
return map[string][]string{}
}WebSocketアダプタの実装
WebSocket用のContext実装です。 core.WebSocketContextを実装します。
// internal/ws/context_impl.go
type WSExecutionContext struct {
ctx context.Context
connID string
path string
messageType int
payload []byte
eventBus publish.EventBus
store map[string]any
}
func NewWSExecutionContext(
ctx context.Context,
connID string,
path string,
messageType int,
payload []byte,
eventBus publish.EventBus,
sendFn func(int, []byte) error,
) core.WebSocketContext {
ctx = context.WithValue(ctx, pkgws.SenderKey, &connSender{send: sendFn})
return &WSExecutionContext{
ctx: ctx,
connID: connID,
path: path,
messageType: messageType,
payload: payload,
eventBus: eventBus,
store: make(map[string]any),
}
}WebSocket Contextの特殊動作
func (w *WSExecutionContext) Method() string {
return "WS"
}
func (w *WSExecutionContext) ConnID() string {
return w.connID
}
func (w *WSExecutionContext) MessageType() int {
return w.messageType
}
func (w *WSExecutionContext) Payload() []byte {
return w.payload
}
func (w *WSExecutionContext) EventBus() core.EventBus {
return w.eventBus
}ArgumentResolverとContext
ArgumentResolver は ExecutionContext を受け取り、必要に応じてプロトコル固有の Context で型断言します。
// internal/resolver/argument.go
type ArgumentResolver interface {
Supports(parameterMeta ParameterMeta) bool
Resolve(ctx core.ExecutionContext, parameterMeta ParameterMeta) (any, error)
}HTTP Resolverの例
// internal/resolver/path_int_resolver.go
func (r *PathIntResolver) Resolve(ctx core.ExecutionContext, parameterMeta ParameterMeta) (any, error) {
// HttpRequestContextに 型 アサーション
httpCtx, ok := ctx.(core.HttpRequestContext)
if !ok {
return nil, fmt.Errorf("HTTP リクエスト コンテキストが ではありません")
}
raw, ok := httpCtx.Params()[parameterMeta.PathKey]
if !ok {
return nil, fmt.Errorf("path paramを 見つかりません. %s", parameterMeta.PathKey)
}
value, err := strconv.ParseInt(raw, 10, 64)
if err != nil {
return nil, err
}
return path.Int{Value: value}, nil
}Consumer Resolverの例
// internal/event/consumer/resolver/event_name_resolver.go
func (r *EventNameResolver) Resolve(ctx core.ExecutionContext, meta resolver.ParameterMeta) (any, error) {
// ConsumerRequestContextに 型 アサーション
consumerCtx, ok := ctx.(core.ConsumerRequestContext)
if !ok {
return nil, fmt.Errorf("ConsumerRequestContextが ではありません")
}
name := consumerCtx.EventName()
if name == "" {
return nil, fmt.Errorf("EventNameを RequestContextで 見つかりません")
}
return name, nil
}WebSocket Resolverの例
// internal/ws/resolver/dto_resolver.go
func (r *DTOResolver) Resolve(ctx core.ExecutionContext, meta resolver.ParameterMeta) (any, error) {
wsCtx, ok := ctx.(core.WebSocketContext)
if !ok {
return nil, fmt.Errorf("WebSocketContextが ではありません")
}
payload := wsCtx.Payload()
if payload == nil {
return nil, fmt.Errorf("Payloadが 空のため DTOを 生成する ことが ありません")
}
dtoPtr := reflect.New(meta.Type)
if err := json.Unmarshal(payload, dtoPtr.Interface()); err != nil {
return nil, fmt.Errorf("DTO デシリアライズ 失敗: %w", err)
}
return dtoPtr.Elem().Interface(), nil
}Common Resolverの例
StdContextResolverは、HTTP、Consumer、WebSocketの両方で動作します。
// internal/resolver/std_context_resolver.go
func (r *StdContextResolver) Resolve(ctx core.ExecutionContext, parameterMeta ParameterMeta) (any, error) {
baseCtx := ctx.Context()
bus := ctx.EventBus()
if bus != nil {
return context.WithValue(baseCtx, publish.PublisherKey, bus), nil
}
return baseCtx, nil
}ControllerContext Resolver
ControllerContextResolver は、ExecutionContext を読み取り専用 ControllerContext でラップします。
// internal/resolver/controller_context_resolver.go
func (r *ControllerContextResolver) Resolve(ctx core.ExecutionContext, _ ParameterMeta) (any, error) {
return runtime.NewControllerContext(ctx), nil
}パイプラインでの使用
Router
// internal/router/router.go
func (r *DefaultRouter) Route(ctx core.ExecutionContext) (core.HandlerMeta, error) {
for _, route := range r.routes {
if route.Method != ctx.Method() {
continue
}
ok, params, keys := matchPath(route.Path, ctx.Path())
if !ok {
continue
}
// マッチした 情報を Contextに 保存
ctx.Set("spine.params", params)
ctx.Set("spine.pathKeys", keys)
return route.Meta, nil
}
return core.HandlerMeta{}, httperr.NotFound("ハンドラーが ありません.")
}Pipeline - Executeフロー
// internal/pipeline/pipeline.go
func (p *Pipeline) Execute(ctx core.ExecutionContext) (finalErr error) {
// 1. グローバル Interceptor PreHandle (ルーティング前)
// 2. Routerが 実行 対象を 決定
// 3. ルート Interceptor PreHandle
// 4. ArgumentResolver チェーン 実行
// 5. Controller Method 呼び出し (Invoker)
// 6. ReturnValueHandler 処理
// 7. PostExecutionHook (イベント 発行 など)
// 8. ルート Interceptor PostHandle (逆順)
// 9. グローバル Interceptor PostHandle (逆順)
// 10. AfterCompletion (成功/失敗 無関係, 逆順)
}Pipeline - ArgumentResolver 呼び出し
// internal/pipeline/pipeline.go
func (p *Pipeline) resolveArguments(ctx core.ExecutionContext, paramMetas []resolver.ParameterMeta) ([]any, error) {
args := make([]any, 0, len(paramMetas))
for _, paramMeta := range paramMetas {
resolved := false
for _, r := range p.argumentResolvers {
if !r.Supports(paramMeta) {
continue
}
// ExecutionContextを 直接 伝達
// Resolver 内部で 必要した 型にに アサーション
val, err := r.Resolve(ctx, paramMeta)
if err != nil {
return nil, err
}
args = append(args, val)
resolved = true
break
}
if !resolved {
return nil, fmt.Errorf(
"ArgumentResolverに parameterが ありません. %d (%s)",
paramMeta.Index,
paramMeta.Type.String(),
)
}
}
return args, nil
}Interceptor
// interceptor/cors/cors.go
func (i *CORSInterceptor) PreHandle(ctx core.ExecutionContext, meta core.HandlerMeta) error {
// ResponseWriter 取得
rwAny, ok := ctx.Get("spine.response_writer")
if !ok {
return nil
}
rw := rwAny.(core.ResponseWriter)
// リクエスト 情報 確認
origin := ctx.Header("Origin")
if origin != "" && i.isAllowedOrigin(origin) {
rw.SetHeader("Access-Control-Allow-Origin", origin)
}
// Preflight 処理
if ctx.Method() == "OPTIONS" {
rw.WriteStatus(204)
return core.ErrAbortPipeline
}
return nil
}内部ストレージ規約
Set()/Get() で使用するキーには明確な規約があります。
Spine予約キー
| キー | タイプ | 設定位置 | 用途 |
|---|---|---|---|
spine.params | map[string]string | Router | Path parameter値 |
spine.pathKeys | []string | Router | パスキーの順序 |
spine.response_writer | core.ResponseWriter | アダプター | 応答出力 |
使用例
// ReturnValueHandlerで ResponseWriter 使用
func (h *JSONReturnHandler) Handle(value any, ctx core.ExecutionContext) error {
rwAny, ok := ctx.Get("spine.response_writer")
if !ok {
return fmt.Errorf("ExecutionContext 内で ResponseWriterを 見つかりません.")
}
rw, ok := rwAny.(core.ResponseWriter)
if !ok {
return fmt.Errorf("ResponseWriter 型が正しくありません.")
}
return rw.WriteJSON(200, value)
}EventBus統合
ExecutionContextにcore.EventBusが統合されています。
Controllerでイベントを発行する
// cmd/demo/controller.go
func (c *UserController) CreateOrder(ctx context.Context, orderId path.Int) string {
// context.Contextで EventBusを 取り出し イベント 発行
publish.Event(ctx, OrderCreated{
OrderID: orderId.Value,
At: time.Now(),
})
return "OK"
}EventBus 注入フロー
// internal/resolver/std_context_resolver.go
func (r *StdContextResolver) Resolve(ctx core.ExecutionContext, parameterMeta ParameterMeta) (any, error) {
baseCtx := ctx.Context()
bus := ctx.EventBus()
if bus != nil {
// EventBusを context.Contextに 注入
return context.WithValue(baseCtx, publish.PublisherKey, bus), nil
}
return baseCtx, nil
}PostExecutionHookからイベントを解放
Pipeline 実行完了後に収集されたイベントを一度に放出します。
// internal/event/hook/post_execution.go
func (h *EventDispatchHook) AfterExecution(ctx core.ExecutionContext, results []any, err error) {
if err != nil {
return
}
events := ctx.EventBus().Drain()
if len(events) == 0 {
return
}
h.Dispatcher.Dispatch(ctx.Context(), events)
}設計原則
1. ControllerはExecutionContextを知らない
コントローラはExecutionContextやHttpRequestContextを直接受け取りません。代わりに、意味タイプ(path.Int、query.Valuesなど)、context.Context、および必要に応じてControllerContextとして値のみを受け取ります。
// ❌ アンチパターン
func (c *UserController) GetUser(ctx core.ExecutionContext) User
// ✓ Spine 方式
func (c *UserController) GetUser(ctx context.Context, userId path.Int) User
// ✓ Interceptor 注入 値は 必要な場合
func (c *UserController) GetUser(ctx context.Context, cc core.ControllerContext, userId path.Int) User2. ResolverはExecutionContextを受け取り、必要な型に断言する
ArgumentResolverはExecutionContextを受け取ります。プロトコル固有の機能が必要な場合は、HttpRequestContext、ConsumerRequestContext、またはWebSocketContextとタイプします。
func (r *PathIntResolver) Resolve(ctx core.ExecutionContext, parameterMeta ParameterMeta) (any, error) {
httpCtx, ok := ctx.(core.HttpRequestContext)
if !ok {
return nil, fmt.Errorf("HTTP リクエスト コンテキストが ではありません")
}
// ...
}3. シングルパイプライン、マルチプロトコル
HTTP、Event Consumer、WebSocketは同じパイプライン構造を共有します。コンテキスト層分離により、各プロトコルの特性をサポートしながら、コードの再利用を最大化します。
// HTTP Pipeline
httpPipeline.AddArgumentResolver(
&resolver.StdContextResolver{}, // 共通
&resolver.ControllerContextResolver{}, // 共通
&resolver.HeaderResolver{}, // HTTP 専用
&resolver.PathIntResolver{}, // HTTP 専用
&resolver.PathStringResolver{}, // HTTP 専用
&resolver.PathBooleanResolver{}, // HTTP 専用
&resolver.PaginationResolver{}, // HTTP 専用
&resolver.QueryValuesResolver{}, // HTTP 専用
&resolver.DTOResolver{}, // HTTP 専用
&resolver.FormDTOResolver{}, // HTTP 専用
&resolver.UploadedFilesResolver{}, // HTTP 専用
)
// Consumer Pipeline
consumerPipeline.AddArgumentResolver(
&resolver.StdContextResolver{}, // 共通
&eventResolver.EventNameResolver{}, // Consumer 専用
&eventResolver.DTOResolver{}, // Consumer 専用
)
// WebSocket Pipeline
wsPipeline.AddArgumentResolver(
&resolver.StdContextResolver{}, // 共通
&wsResolver.ConnectionIDResolver{}, // WebSocket 専用
&wsResolver.DTOResolver{}, // WebSocket 専用
)まとめ
|インターフェース|役割|主な方法使用場所| |-----------|------|------------|----------| | ContextCarrier | Go context配信| Context() |どこでも | EventBusCarrier |イベント発行(core.EventBus)| EventBus() |コントローラー、コンシューマー | ExecutionContext |実行フロー制御| Method()、Path()、Header()、Set()、Get() |ルーター、パイプライン、インターセプター| | ControllerContext | ExecutionContext読み取り専用Facade | Get() | Controller | | HttpRequestContext | HTTP入力の解釈Param()、Query()、Header()、Headers()、Bind()、MultipartForm() | HTTP ArgumentResolver | | ConsumerRequestContext |イベント入力の解釈EventName()、Payload() | Consumer ArgumentResolver | | WebSocketContext | WebSocket入力解析| ConnID()、MessageType()、Payload() | WebSocket ArgumentResolver |
核心原則:Context階層分離により、HTTP、Event Consumer、WebSocketは同じパイプラインモデルを共有します。 Controllerは実行モデルをまったく知らず、ビジネスロジックだけに集中しています。
