Skip to content

core.ExecutionContext

API reference for ExecutionContext.

Overview

ExecutionContext is an interface that allows access to request information within the Spine pipeline and shares data between components. It is a unified execution context that handles both HTTP requests and event messages.

go
import "github.com/NARUBROWN/spine/core"

Interface Hierarchy

Spine separates Context hierarchically.

ContextCarrier ──────┬──► RequestContext ──┬──► HttpRequestContext
                     │                     │
EventBusCarrier ─────┤                     └──► ConsumerRequestContext

                     └──► ExecutionContext
InterfaceRoleUsage Location
ContextCarrierDeliver Go contextEverywhere
EventBusCarrierPublish eventsController, Consumer
RequestContextResolver minimum contractArgumentResolver base
ExecutionContextControl execution flowRouter, Pipeline, Interceptor
HttpRequestContextInterpret HTTP inputHTTP ArgumentResolver
ConsumerRequestContextInterpret event inputConsumer ArgumentResolver

Interface Definition

Base Interface

go
type ContextCarrier interface {
    Context() context.Context
}

type EventBusCarrier interface {
    EventBus() publish.EventBus
}

type RequestContext interface {
    ContextCarrier
    EventBusCarrier
}

WebSocketContext

Extended interface dedicated to WebSocket. Embeds ExecutionContext.

go
type WebSocketContext interface {
    ExecutionContext

    ConnID() string
    MessageType() int
    Payload() []byte
}

ExecutionContext

go
type ExecutionContext interface {
    ContextCarrier
    EventBusCarrier

    Method() string
    Path() string
    Header(name string) string
    Params() map[string]string
    PathKeys() []string
    Queries() map[string][]string
    Set(key string, value any)
    Get(key string) (any, bool)
}

HttpRequestContext

Extended interface dedicated to HTTP.

go
type HttpRequestContext interface {
    RequestContext

    Param(name string) string
    Query(name string) string
    Params() map[string]string
    Queries() map[string][]string
    Bind(out any) error
    MultipartForm() (*multipart.Form, error)
}

ConsumerRequestContext

Extended interface dedicated to Event Consumers.

go
type ConsumerRequestContext interface {
    RequestContext

    EventName() string
    Payload() []byte
}

ExecutionContext Methods

Context

go
Context() context.Context

Returns context.Context from the Go standard library.

Returns

  • context.Context - Request scope context

Example

go
func (i *TimeoutInterceptor) PreHandle(ctx core.ExecutionContext, meta core.HandlerMeta) error {
    select {
    case <-ctx.Context().Done():
        return ctx.Context().Err()
    default:
        return nil
    }
}

EventBus

go
EventBus() core.EventBus

Returns the request-scoped EventBus. Used for publishing domain events.

Returns

  • core.EventBus - Event bus instance

Example

go
// Drain events in PostExecutionHook
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)
}

Method

go
Method() string

Returns the request method.

Returns

  • HTTP: "GET", "POST", "PUT", "DELETE", etc.
  • Consumer: "EVENT"
  • WebSocket: "WS"

Example

go
func (i *CORSInterceptor) PreHandle(ctx core.ExecutionContext, meta core.HandlerMeta) error {
    if ctx.Method() == "OPTIONS" {
        // Handle Preflight request
    }
    return nil
}

Path

go
Path() string

Returns the request path.

Returns

  • HTTP: Request path (e.g. "/users/123")
  • Consumer: Event name (e.g. "order.created")
  • WebSocket: WebSocket path (e.g. "/ws/chat")

Example

go
log.Printf("[REQ] %s %s", ctx.Method(), ctx.Path())
// HTTP:      [REQ] GET /users/123
// Consumer:  [REQ] EVENT order.created
// WebSocket: [REQ] WS /ws/chat
go
Header(name string) string

Returns the HTTP header value for the specified name.

Parameters

  • name - Header name (case-insensitive)

Returns

  • string - Header value. Empty string if not found.
  • Always empty string in Consumer/WebSocket.

Example

go
origin := ctx.Header("Origin")
auth := ctx.Header("Authorization")

Params

go
Params() map[string]string

Returns all path parameters as a map.

Returns

  • map[string]string - Path parameter map
  • Empty map in Consumer/WebSocket

Example

go
// Route: /users/:userId/posts/:postId
// Request: /users/123/posts/456

params := ctx.Params()  // {"userId": "123", "postId": "456"}

PathKeys

go
PathKeys() []string

Returns path parameter keys in declaration order.

Returns

  • []string - Key slice
  • Empty slice in Consumer/WebSocket

Example

go
// Route: /users/:userId/posts/:postId

ctx.PathKeys()  // ["userId", "postId"]

Queries

go
Queries() map[string][]string

Returns all query parameters as a map.

Returns

  • map[string][]string - Query parameter map
  • Empty map in Consumer/WebSocket

Example

go
// Request: /users?status=active&tag=go&tag=web

queries := ctx.Queries()
// {"status": ["active"], "tag": ["go", "web"]}

Set

go
Set(key string, value any)

Stores a value in the internal storage.

Parameters

  • key - Key to store
  • value - Value to store

Example

go
ctx.Set("auth.user", authenticatedUser)
ctx.Set("request.startTime", time.Now())

Get

go
Get(key string) (any, bool)

Retrieves a value from the internal storage.

Parameters

  • key - Key to retrieve

Returns

  • any - Stored value
  • bool - Whether the key exists

Example

go
if rw, ok := ctx.Get("spine.response_writer"); ok {
    responseWriter := rw.(core.ResponseWriter)
}

ControllerContext

Controller-specific view used to read values injected by Interceptors.

Get

go
Get(key string) (any, bool)

Delegates to ExecutionContext.Get(). Does not provide Set() method.

Implementation

go
// 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)
}

Example

go
func (c *UserController) GetUser(ctx core.ControllerContext, userId path.Int) (User, error) {
    // Read value injected by Interceptor with Set("auth.user", ...)
    if authUser, ok := ctx.Get("auth.user"); ok {
        user := authUser.(*AuthUser)
        // ...
    }
    return c.repo.FindByID(userId.Value)
}

HttpRequestContext Methods

Additional methods used in HTTP ArgumentResolver.

Param

go
Param(name string) string

Returns the value of a specific path parameter.

Example

go
userId := ctx.Param("id")  // "123"

Query

go
Query(name string) string

Returns the first value of a specific query parameter.

Example

go
page := ctx.Query("page")  // "1"

Headers

go
Headers() map[string][]string

Returns all HTTP headers as a map.

Example

go
headers := ctx.Headers()
// {"Content-Type": ["application/json"], "Accept": ["text/html", "application/json"]}

Bind

go
Bind(out any) error

Binds the HTTP body to a struct.

Example

go
var req CreateUserRequest
if err := ctx.Bind(&req); err != nil {
    return err
}

MultipartForm

go
MultipartForm() (*multipart.Form, error)

Accesses Multipart form data.

Example

go
form, err := ctx.MultipartForm()
if err != nil {
    return err
}
for _, file := range form.File["upload"] {
    // Process file
}

ConsumerRequestContext Methods

Methods used in Event Consumer ArgumentResolver.

EventName

go
EventName() string

Returns the name of the received event.

Example

go
name := ctx.EventName()  // "order.created"

Payload

go
Payload() []byte

Returns the raw payload of the event.

Example

go
payload := ctx.Payload()  // []byte (JSON)
var event OrderCreated
json.Unmarshal(payload, &event)

WebSocketContext Methods

Methods used in WebSocket ArgumentResolver.

ConnID

go
ConnID() string

Returns the unique identifier of the WebSocket connection.

Example

go
connID := ctx.ConnID()  // "a1b2c3d4-..."

MessageType

go
MessageType() int

Returns the WebSocket message type.

Returns

  • 1 - TextMessage
  • 2 - BinaryMessage

Example

go
if ctx.MessageType() == ws.TextMessage {
    // Handle text message
}

Payload

go
Payload() []byte

Returns the raw payload of the WebSocket message.

Example

go
payload := ctx.Payload()  // []byte
var msg ChatMessage
json.Unmarshal(payload, &msg)

Reserved Keys

KeyTypeDescription
spine.response_writercore.ResponseWriterResponse output interface
spine.paramsmap[string]stringPath parameters
spine.pathKeys[]stringPath parameter key order

Usage in Interceptor

ExecutionContext is passed as the first argument in all Interceptor methods.

go
type Interceptor interface {
    PreHandle(ctx ExecutionContext, meta HandlerMeta) error
    PostHandle(ctx ExecutionContext, meta HandlerMeta)
    AfterCompletion(ctx ExecutionContext, meta HandlerMeta, err error)
}

Logging Example

go
type LoggingInterceptor struct{}

func (i *LoggingInterceptor) PreHandle(ctx core.ExecutionContext, meta core.HandlerMeta) error {
    log.Printf("[REQ] %s %s -> %s.%s",
        ctx.Method(),
        ctx.Path(),
        meta.ControllerType.Name(),
        meta.Method.Name,
    )
    return nil
}

func (i *LoggingInterceptor) PostHandle(ctx core.ExecutionContext, meta core.HandlerMeta) {
    log.Printf("[RES] %s %s OK", ctx.Method(), ctx.Path())
}

func (i *LoggingInterceptor) AfterCompletion(ctx core.ExecutionContext, meta core.HandlerMeta, err error) {
    if err != nil {
        log.Printf("[ERR] %s %s : %v", ctx.Method(), ctx.Path(), err)
    }
}

CORS Example

go
type CORSInterceptor struct {
    config Config
}

func (i *CORSInterceptor) PreHandle(ctx core.ExecutionContext, meta core.HandlerMeta) error {
    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)
        rw.SetHeader("Vary", "Origin")
    }
    
    if ctx.Method() == "OPTIONS" {
        rw.WriteStatus(204)
        return core.ErrAbortPipeline
    }
    
    return nil
}

Usage in ArgumentResolver

ArgumentResolver receives core.ExecutionContext and type assertions it to a protocol-specific Context as needed.

HTTP Resolver Example

go
func (r *PathIntResolver) Resolve(ctx core.ExecutionContext, parameterMeta ParameterMeta) (any, error) {
    // Type assert to HttpRequestContext
    httpCtx, ok := ctx.(core.HttpRequestContext)
    if !ok {
        return nil, fmt.Errorf("Not an HTTP request context")
    }

    raw, ok := httpCtx.Params()[parameterMeta.PathKey]
    if !ok {
        return nil, fmt.Errorf("path param not found: %s", parameterMeta.PathKey)
    }

    value, err := strconv.ParseInt(raw, 10, 64)
    if err != nil {
        return nil, err
    }

    return path.Int{Value: value}, nil
}

Consumer Resolver Example

go
func (r *EventNameResolver) Resolve(ctx core.ExecutionContext, meta ParameterMeta) (any, error) {
    // Type assert to ConsumerRequestContext
    consumerCtx, ok := ctx.(core.ConsumerRequestContext)
    if !ok {
        return nil, fmt.Errorf("Not a ConsumerRequestContext")
    }

    return consumerCtx.EventName(), nil
}

WebSocket Resolver Example

go
func (r *ConnectionIDResolver) Resolve(ctx core.ExecutionContext, meta ParameterMeta) (any, error) {
    // Type assert to WebSocketContext
    wsCtx, ok := ctx.(core.WebSocketContext)
    if !ok {
        return nil, fmt.Errorf("Not a WebSocketContext")
    }

    return ws.ConnectionID{Value: wsCtx.ConnID()}, nil
}

Common Resolver Example

Resolver that works for HTTP, Consumer, and WebSocket.

go
func (r *StdContextResolver) Resolve(ctx core.ExecutionContext, parameterMeta ParameterMeta) (any, error) {
    baseCtx := ctx.Context()
    bus := ctx.EventBus()
    if bus != nil {
        // Inject EventBus into context.Context
        return context.WithValue(baseCtx, publish.PublisherKey, bus), nil
    }
    return baseCtx, nil
}

ControllerContext Resolver Example

go
func (r *ControllerContextResolver) Resolve(ctx core.ExecutionContext, _ ParameterMeta) (any, error) {
    return runtime.NewControllerContext(ctx), nil
}

Protocol-Specific Behavior Difference

MethodHTTPConsumerWebSocket
Method()"GET", "POST", etc."EVENT""WS"
Path()/users/123order.created/ws/chat
Header()Header valueEmpty stringEmpty string
Params()path paramsEmpty mapEmpty map
PathKeys()key orderEmpty sliceEmpty slice
Queries()query paramsEmpty mapEmpty map
EventBus()EventBusEventBusEventBus
Context()Request contextRequest contextRequest context

Implementations

ImplementationInterfaceLocation
echoContextExecutionContext + HttpRequestContextinternal/adapter/echo/context_impl.go
ConsumerRequestContextImplExecutionContext + ConsumerRequestContextinternal/event/consumer/request_context_impl.go
WSExecutionContextWebSocketContext (⊃ ExecutionContext)internal/ws/context_impl.go
controllerCtxViewControllerContextinternal/runtime/controller_ctx.go

See Also