📦 Context - oCtx¶
When HIX executes a middleware, it always passes a single parameter: oCtx.
It's the request context - an instance of THixContext that groups everything
a middleware needs to inspect the request, communicate with other middleware links in the chain,
and decide what should happen next.
FUNCTION MW_ApiKey( oCtx )
// oCtx is the context - it lives throughout the entire middleware chain
// and disappears when the request ends
RETURN .T.
Why does oCtx exist instead of just oReq?¶
A middleware rarely acts alone. In a typical chain (rate limit → JWT → roles → action), each middleware needs to share information with the next ones: the JWT payload, the user's session, an audit flag…
oCtx is that "shared space". It's the tray that gets passed hand to hand
throughout the chain, and when it reaches the route action it remains available.
Main properties¶
| Property | Type | Description |
|---|---|---|
oCtx:oReq |
THixRequest |
Request object of the current request. |
oCtx:hData |
Hash | Free dictionary to share data between middlewares. |
oCtx:lHandled |
Logical | Mark it .T. when the middleware has already responded. |
oCtx:cScope |
String | Free metadata assigned to the route (accessible from MW). |
oCtx:cOnFail |
String | Redirect URL if the middleware returns .F. (optional). |
oCtx:oReq - the request¶
It's the THixRequest object of the current request. We can read headers,
cookies, body, query params, etc. from there:
LOCAL cKey := oCtx:oReq:Header( "X-Api-Key", "" )
LOCAL cSid := oCtx:oReq:Cookie( "hix_sess", "" )
LOCAL cIp := oCtx:oReq:IP()
Recommended alternative - U* helpers¶
HIX automatically links the request to the current thread before executing each
middleware, so the U* helpers also work inside the middleware,
which are usually shorter and more consistent with action code:
FUNCTION MW_ApiKey( oCtx )
// Both lines are equivalent:
LOCAL cKey := oCtx:oReq:Header( "X-Api-Key", "" )
LOCAL cKey := UHeader( "X-Api-Key", "" ) // shorter and more readable
IF cKey != "clave-secreta-123"
USendError( 401, "Invalid API Key" )
RETURN .F.
ENDIF
RETURN .T.
Style with oCtx:oReq |
Style with U* |
|---|---|
oCtx:oReq:Header( c, x ) |
UHeader( c, x ) |
oCtx:oReq:Cookie( c, x ) |
UCookie( c, x ) |
oCtx:oReq:Body() |
UBody() |
oCtx:oReq:IP() |
UIP() |
oCtx:oReq:Method() |
UMethod() |
Choose the style you prefer - HIX doesn't impose any one. The current convention is
to use U* inside actions and middlewares to keep the code concise.
oCtx:hData - sharing data between middlewares¶
hData is a free hash that propagates through the entire middleware chain and
arrives intact at the route action. It's the official channel for passing
information between links.
Conventional keys already used by system middlewares:
| Key | Set by | Content |
|---|---|---|
oCtx:hData["jwt"] |
HixMwJwt |
Hash with the payload of the verified JWT token. |
oCtx:hData["session"] |
HixMwSession |
Hash with the active session data. |
oCtx:hData["_sid"] |
HixMwSession |
ID of the active session. |
oCtx:hData["user"] |
Auth middleware | Object/hash of the authenticated user. |
Example - a roles middleware that reads what HixMwJwt already left:
FUNCTION MW_RequireAdmin( oCtx )
LOCAL hJwt := hb_HGetDef( oCtx:hData, "jwt", NIL )
IF hJwt == NIL .OR. hb_HGetDef( hJwt, "role", "" ) != "admin"
USendError( 403, "Admins only" )
RETURN .F.
ENDIF
RETURN .T.
You can add your own keys without touching anything in the system:
oCtx:lHandled - "I already responded, don't execute the action"¶
When a middleware decides to break the chain (reject the request) it must:
- Send the response to the client.
- Mark
oCtx:lHandled := .T.so the dispatcher knows the response has already been sent and doesn't execute anything else. - Return
.F..
If you use the USendError / USendJson / URedirect helpers, they already mark
lHandled internally - you don't need to do it manually.
FUNCTION MW_ApiKey( oCtx )
IF UHeader( "X-Api-Key", "" ) != "clave-secreta-123"
USendError( 401, "Invalid API Key" ) // marks lHandled
RETURN .F.
ENDIF
RETURN .T.
oCtx:cScope - route metadata¶
When you register a route you can attach it a free string as scope. That
value reaches the middleware via oCtx:cScope and serves to vary the behavior
according to the "logical group" to which the route belongs.
oSrv:AddRouteGet( "admin.users", "/admin/users", 'users.prg', "MW_Log", "admin" )
oSrv:AddRouteGet( "api.stats", "/api/stats", 'stats.prg', "MW_Log", "public" )
FUNCTION MW_Log( oCtx )
IF oCtx:cScope == "admin"
l( "[AUDIT] " + UMethod() + " " + UPath() + " by " + UIP() )
ENDIF
RETURN .T.
oCtx:cOnFail - fallback redirect¶
Route to redirect to automatically when the middleware returns .F.
(optional). Useful, for example, to send to /login any route that fails
authentication without repeating the logic in each middleware.
Summary¶
oCtxis the request context, the single parameter received by every middleware.oCtx:oReqprovides access to the request; alternatively you can use theU*helpers.oCtx:hDatais the hash shared between middlewares and the action.oCtx:lHandled := .T.when you break the chain; theUSend*helpers already do it.oCtx:cScopeis free metadata of the route.oCtx:cOnFaildefines the redirect URL if the middleware rejects.