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.
import "github.com/NARUBROWN/spine/core"Interface Hierarchy
Spine separates Context hierarchically.
ContextCarrier ──────┬──► RequestContext ──┬──► HttpRequestContext
│ │
EventBusCarrier ─────┤ └──► ConsumerRequestContext
│
└──► ExecutionContext| Interface | Role | Usage Location |
|---|---|---|
ContextCarrier | Deliver Go context | Everywhere |
EventBusCarrier | Publish events | Controller, Consumer |
RequestContext | Resolver minimum contract | ArgumentResolver base |
ExecutionContext | Control execution flow | Router, Pipeline, Interceptor |
HttpRequestContext | Interpret HTTP input | HTTP ArgumentResolver |
ConsumerRequestContext | Interpret event input | Consumer ArgumentResolver |
Interface Definition
Base Interface
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.
type WebSocketContext interface {
ExecutionContext
ConnID() string
MessageType() int
Payload() []byte
}ExecutionContext
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.
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.
type ConsumerRequestContext interface {
RequestContext
EventName() string
Payload() []byte
}ExecutionContext Methods
Context
Context() context.ContextReturns context.Context from the Go standard library.
Returns
context.Context- Request scope context
Example
func (i *TimeoutInterceptor) PreHandle(ctx core.ExecutionContext, meta core.HandlerMeta) error {
select {
case <-ctx.Context().Done():
return ctx.Context().Err()
default:
return nil
}
}EventBus
EventBus() core.EventBusReturns the request-scoped EventBus. Used for publishing domain events.
Returns
core.EventBus- Event bus instance
Example
// 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
Method() stringReturns the request method.
Returns
- HTTP:
"GET","POST","PUT","DELETE", etc. - Consumer:
"EVENT" - WebSocket:
"WS"
Example
func (i *CORSInterceptor) PreHandle(ctx core.ExecutionContext, meta core.HandlerMeta) error {
if ctx.Method() == "OPTIONS" {
// Handle Preflight request
}
return nil
}Path
Path() stringReturns 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
log.Printf("[REQ] %s %s", ctx.Method(), ctx.Path())
// HTTP: [REQ] GET /users/123
// Consumer: [REQ] EVENT order.created
// WebSocket: [REQ] WS /ws/chatHeader
Header(name string) stringReturns 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
origin := ctx.Header("Origin")
auth := ctx.Header("Authorization")Params
Params() map[string]stringReturns all path parameters as a map.
Returns
map[string]string- Path parameter map- Empty map in Consumer/WebSocket
Example
// Route: /users/:userId/posts/:postId
// Request: /users/123/posts/456
params := ctx.Params() // {"userId": "123", "postId": "456"}PathKeys
PathKeys() []stringReturns path parameter keys in declaration order.
Returns
[]string- Key slice- Empty slice in Consumer/WebSocket
Example
// Route: /users/:userId/posts/:postId
ctx.PathKeys() // ["userId", "postId"]Queries
Queries() map[string][]stringReturns all query parameters as a map.
Returns
map[string][]string- Query parameter map- Empty map in Consumer/WebSocket
Example
// Request: /users?status=active&tag=go&tag=web
queries := ctx.Queries()
// {"status": ["active"], "tag": ["go", "web"]}Set
Set(key string, value any)Stores a value in the internal storage.
Parameters
key- Key to storevalue- Value to store
Example
ctx.Set("auth.user", authenticatedUser)
ctx.Set("request.startTime", time.Now())Get
Get(key string) (any, bool)Retrieves a value from the internal storage.
Parameters
key- Key to retrieve
Returns
any- Stored valuebool- Whether the key exists
Example
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
Get(key string) (any, bool)Delegates to ExecutionContext.Get(). Does not provide Set() method.
Implementation
// 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
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
Param(name string) stringReturns the value of a specific path parameter.
Example
userId := ctx.Param("id") // "123"Query
Query(name string) stringReturns the first value of a specific query parameter.
Example
page := ctx.Query("page") // "1"Headers
Headers() map[string][]stringReturns all HTTP headers as a map.
Example
headers := ctx.Headers()
// {"Content-Type": ["application/json"], "Accept": ["text/html", "application/json"]}Bind
Bind(out any) errorBinds the HTTP body to a struct.
Example
var req CreateUserRequest
if err := ctx.Bind(&req); err != nil {
return err
}MultipartForm
MultipartForm() (*multipart.Form, error)Accesses Multipart form data.
Example
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
EventName() stringReturns the name of the received event.
Example
name := ctx.EventName() // "order.created"Payload
Payload() []byteReturns the raw payload of the event.
Example
payload := ctx.Payload() // []byte (JSON)
var event OrderCreated
json.Unmarshal(payload, &event)WebSocketContext Methods
Methods used in WebSocket ArgumentResolver.
ConnID
ConnID() stringReturns the unique identifier of the WebSocket connection.
Example
connID := ctx.ConnID() // "a1b2c3d4-..."MessageType
MessageType() intReturns the WebSocket message type.
Returns
1- TextMessage2- BinaryMessage
Example
if ctx.MessageType() == ws.TextMessage {
// Handle text message
}Payload
Payload() []byteReturns the raw payload of the WebSocket message.
Example
payload := ctx.Payload() // []byte
var msg ChatMessage
json.Unmarshal(payload, &msg)Reserved Keys
| Key | Type | Description |
|---|---|---|
spine.response_writer | core.ResponseWriter | Response output interface |
spine.params | map[string]string | Path parameters |
spine.pathKeys | []string | Path parameter key order |
Usage in Interceptor
ExecutionContext is passed as the first argument in all Interceptor methods.
type Interceptor interface {
PreHandle(ctx ExecutionContext, meta HandlerMeta) error
PostHandle(ctx ExecutionContext, meta HandlerMeta)
AfterCompletion(ctx ExecutionContext, meta HandlerMeta, err error)
}Logging Example
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
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
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
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
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.
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
func (r *ControllerContextResolver) Resolve(ctx core.ExecutionContext, _ ParameterMeta) (any, error) {
return runtime.NewControllerContext(ctx), nil
}Protocol-Specific Behavior Difference
| Method | HTTP | Consumer | WebSocket |
|---|---|---|---|
Method() | "GET", "POST", etc. | "EVENT" | "WS" |
Path() | /users/123 | order.created | /ws/chat |
Header() | Header value | Empty string | Empty string |
Params() | path params | Empty map | Empty map |
PathKeys() | key order | Empty slice | Empty slice |
Queries() | query params | Empty map | Empty map |
EventBus() | EventBus | EventBus | EventBus |
Context() | Request context | Request context | Request context |
Implementations
| Implementation | Interface | Location |
|---|---|---|
echoContext | ExecutionContext + HttpRequestContext | internal/adapter/echo/context_impl.go |
ConsumerRequestContextImpl | ExecutionContext + ConsumerRequestContext | internal/event/consumer/request_context_impl.go |
WSExecutionContext | WebSocketContext (⊃ ExecutionContext) | internal/ws/context_impl.go |
controllerCtxView | ControllerContext | internal/runtime/controller_ctx.go |
See Also
- Interceptor - Cross-cutting concerns
- Execution Context Concepts - Detailed explanation
