Route Middleware
Attach middleware to a route: closures, WireBox IDs, or plain objects that run before or after the matched handler.
.middleware() attaches a target to a route that runs at request time, scoped to just that route. It's not a new subsystem - it reuses the same interception dispatch ColdBox interceptors already use, just scoped to one route instead of the whole app.
route( "/admin/:action" )
.middleware( ( event, rc, prc ) => {
if ( !auth.isLoggedIn() ) {
event.relocate( "login" );
return true; // stop the remaining middleware for this route
}
} )
.toHandler( "admin" );A Target Can Be
A closure/lambda -
( event, rc, prc ) => { ... }, as aboveA WireBox ID - resolved via
getInstance()on every request, so it respects whatever scope (singleton, prototype, etc) the mapping was registered withAny object - WireBox-managed or not, as long as it has a method named after the point it runs at (
preProcess()by default). No base class or interface requiredA registered name - a name given to any of the above with
registerMiddleware(), or amiddlewareGroup()name
// A concrete class, resolved by WireBox ID
class singleton {
property name="auth" inject="AuthService";
function preProcess( event, rc, prc ){
if ( !auth.isLoggedIn() ) {
event.relocate( "login" );
return true;
}
}
}
// registered in WireBox as "RequireLogin"
route( "/admin/:action" ).middleware( "RequireLogin" ).toHandler( "admin" );// A concrete class, resolved by WireBox ID
component singleton {
property name="auth" inject="AuthService";
function preProcess( event, rc, prc ){
if ( !auth.isLoggedIn() ) {
event.relocate( "login" );
return true;
}
}
}
// registered in WireBox as "RequireLogin"
route( "/admin/:action" ).middleware( "RequireLogin" ).toHandler( "admin" );Where It Runs
A target attaches to a point - preProcess (default) or postProcess - the second argument to .middleware():
Route middleware runs after the global preProcess announce and before the global postProcess announce - global interceptors stay the outermost layer, route-specific work happens closest to the handler.
Route middleware also runs in integration tests: BaseTestCase.execute() and the get(), post() and other HTTP helpers run it in the same order (since ColdBox 8.3.0). See The execute() Method.
Multiple Targets
Pass an array to attach several targets to the same point in one call. They run in order:
Short-Circuiting
Returning true from a target stops the remaining middleware for that route at that point. It does not, by itself, skip the handler or the render - call event.relocate(), event.renderData().noExecution(), or similar, exactly as you would from any other preProcess/postProcess interceptor:
Sharing Middleware Across Routes
Attaching middleware to a group applies it to every route declared inside, ahead of each route's own middleware:
Nested groups compose outer-first:
Registering Named Middleware
registerMiddleware( name, target ) gives a single closure, lambda, object, or WireBox ID a name, so you can reference it from many routes without repeating it. The target is any of the targets above, and the optional third argument is the point (preProcess by default, or postProcess).
A registered name works anywhere a middleware group name does: in .middleware(), in a group's middleware option, and in withoutMiddleware( name ). Under the hood it is stored as a one-member group in the same namespace, and registerMiddleware() returns the router so calls can be chained.
Registering Several at Once
Pass a struct of name : target pairs. Every entry uses the same point (preProcess unless you pass one):
Duplicate Names
Registering a name that is already taken, either by another registerMiddleware() call or by a middlewareGroup(), throws a Router.DuplicateMiddleware exception. Pass force = true to replace the existing definition. A bulk call is all or nothing: if any name is a duplicate, nothing from that call is registered. A missing or empty name or target throws Router.InvalidMiddleware.
Middleware From Modules
The registry belongs to the router that registered it, and modules do not share one. A module that ships middleware should register it as a WireBox mapping and let apps reference the WireBox ID, as Authenticated@cbsecurity does. An application can then give that ID a short local name:
Securing Routes With cbsecurity
The cbsecurity module ships ready-made middleware, so you do not write login and permission checks yourself. Permissions and roles are declared in the route's meta():
cbsecurity Middleware Reference
Every cbsecurity middleware is a WireBox ID you pass to middleware(). Parameters go in the route's meta(), because modules load after the router. Each name links to its cbsecurity guide.
WireBox ID
What it does
Route meta() keys
Requires an API key from the x-api-key header or apiKey request key.
apiKeys, apiKeyHeader, apiKeyParam
Stack them. They run in the order you list them, so put cheap checks first:
The authentication middleware send denied requests through the cbsecurity firewall's invalid action (redirect, override or block). The others answer directly with a JSON error and the right status code.
Defaults for these middleware (trusted proxies, API key header, throttle limiters, the signing secret) live in the cbsecurity route middleware settings. The full guide is the cbsecurity Route Middleware page.
Testing Routes With Middleware
The integration test helpers (execute(), get(), post() and friends) run route middleware in the same order as a real request, so you can assert on a blocked or redirected route.
Last updated
Was this helpful?