Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
The ColdBox Routing DSL will be used to register routes for your application, which exists in your application or module router object. Routing takes place using several methods inside the router, which are divided into the following 3 categories:
Initiators - Starts a URL pattern registration, but does not fully register the route until a terminator is called (target).
Modifiers - Modifies the pattern with extra metdata to listen to from the incoming request.
Terminators - Finalizes the registration process usually by telling the router what happens when the route pattern is detected. This is refered to as the target.
Please note that order of declaration of the routes is imperative. Order matters.
Please remember to check out the latest API Docs for the latest methods and argument signatures.
The following methods are used to initiate a route registration process.
Please note that a route will not register unless a terminator is called or the inline target terminator is passed.
route( pattern, [target], [name=arguments.pattern] )
- Register a new route with optional target terminators and a name
get( pattern, [target], [name] )
- Register a new route with optional target terminators, a name and a GET http verb restriction
post( pattern, [target], [name] )
- Register a new route with optional target terminators, a name and a POST http verb restriction
put( pattern, [target], [name] )
- Register a new route with optional target terminators, a name and a PUT http verb restriction
patch( pattern, [target], [name] )
- Register a new route with optional target terminators, a name and a PATCH http verb restriction
options( pattern, [target], [name] )
- Register a new route with optional target terminators, a name and a OPTIONS http verb restriction
group( struct options, body )
- Group routes together with options that will be applied to all routes declared in the body
closure/lambda.
Modifiers will tell the routing service about certain restrictions, conditions or locations for the routing process. It will not register the route just yet.
header( name, value, overwrite=true )
- attach a response header if the route matches
headers( map, overwrite=true )
- attach multiple response headers if the route matches
as( name )
- Register the route as a named route
rc( name, value, overwrite=true )
- Add an RC
value if the route matched
rcAppend map, overwrite=true )
- Add multiple values to the RC
collection if the route matched
prc( name, value, overwrite=true )
- Add an PRC
value if the route matched
prcAppend map, overwrite=true )
- Add multiple values to the PRC
collection if the route matched
withHandler( handler )
- Map the route to execute a handler
withAction( action )
- Map the route to execute a single action or a struct that represents verbs and actions
withModule( module )
- Map the route to a module
withNamespace( namespace )
- Map the route to a namespace
withSSL()
- Force SSL
withCondition( condition )
- Apply a runtime closure/lambda enclosure
withDomain( domain )
- Map the route to a domain or subdomain
withVerbs( verbs )
- Restrict the route to listen to only these HTTP Verbs
packageResolver( toggle )
- Turn on/off convention for packages
valuePairTranslator( toggle )
- Turn on/off automatic name value pair translations
Terminators finalize the routing process by registering the route in the Router.
end()
- Register the route as it exists
toAction( action )
- Send the route to a specific action or RESTFul action struct
toView( view, layout, noLayout=false, viewModule, layoutModule )
- Send the route to a view/layout
toRedirect( target, statusCode=301 )
- Relocate the route to another event
to( event )
- Execute the event if the route matches
toHandler( handler )
- Execute the handler if the route matches
toResponse( body, statusCode=200, statusText="ok" )
- Inline response action
toModuleRouting( module )
- Send to the module router for evaluation
toNamespaceRouting( namespace )
- Send to the namespace router for evaluation
In your URL pattern you can also use the :
syntax to denote a variable placeholder. These position holders are alphanumeric by default:
Once a URL is matched to the route above, the placeholders (:year/:month?/:day?
) will become request collection (RC
) variables:
Sometimes we will want to declare routes that are very similar in nature and since order matters, they need to be declared in the right order. Like this one:
However, we just wrote 4 routes for this approach when we can just use optional variables by using the ?
symbol at the end of the placeholder. This tells the processor to create the routes for you in the most detailed manner first instead of you doing it manually.
Caution Just remember that an optional placeholder cannot be followed by a non-optional one. It doesn't make sense.
ColdBox gives you also the ability to declare numerical only routes by appending -numeric
to the variable placeholder so the route will only match if the placeholder is numeric. Let's modify the route from above.
This route will only accept years, months and days as numbers.
ColdBox gives you also the ability to declare alpha only routes by appending -alpha
to the variable placeholder so the route will only match if the placeholder is alpha
only.
This route will only accept page names that are alpha only.
There are two ways to place a regex constraint on a placeholder, using the -regex:
placeholder or adding a constraints
structure to the route declaration.
You can also have the ability to declare a placeholder that must match a regular expression by using the -regex( {regex_here} )
placeholder.
The rc
variable format
must match the regex supplied: (xml|json)
You can also apply a structure of regular expressions to a route instead of inlining the regular expressions in the placeholder location. You will do this using the constraints()
method of the router.
The key in the structure must match the name of the placeholder and the value is a regex expression that must be enclosed by parenthesis ()
.
Every router has a default route already defined for you in the application templates, which we refer to as routing by convention:
The URL pattern in the default route includes two special position placeholders, meaning that the handler and the action will come from the URL. Also note that the :action
has a question mark (?
), which makes the placeholder optional, meaning it can exist or not from the incoming URL.
:handler
- The handler to execute (It can include a Package and/or Module reference)
:action
- The action to relocate to (See the ?
, this means that the action is optional)
Behind the scenes the router creates two routes due to the optional placeholder in the following order:
route( "/:handler/:action" )
route( "/:handler)
Tip The :handler
parameter allows you to nest module names and handler names. Ex: /module/handler/action
If no action is passed the default action is index
This route can handle pretty much all your needs by convention:
Any extra name-value pairs in the remaining URL of a discovered URL pattern will be translated to variables in the request collection (rc
) for you automagically.
Tip: You can turn this feature off by using the valuePairTranslation( false )
modifier in the routing DSL on a route by route basis
route( "/pattern" )
.to( "users.show" )
.valuePairTranslation( false );
Apart from routing by convention, you can also register your own expressive routes. Let's investigate the routing approaches.
The route()
method allows you to register a pattern and immediately assign it to execute an event or a response via the target argument.
The first pattern registers and if matched it will execute the wiki.page event. The second pattern if matched it will execute the profile.show event from the users module and register the route with the userprofile name.
You can also pass in a closure or lambda to the target argument and it will be treated as an inline action:
You can also pass just an HTML string with {rc_var}
replacements for the routed variables place in the request collection
to
EventsIf you will not use the inline terminators you can do a full expressive route definition to events using the to()
method, which allows you to concatenate the route pattern with modifiers:
You can also route to a handler and an action using the modifiers instead of the to()
method. This long-form is usually done for visibility or dynamic writing of routes. You can use the following methods:
withHandler()
withAction()
toHandler()
end()
You can also route to views and view/layout combinations by using the toView()
terminator:
You can also use the toRedirect()
method to re-route patterns to other patterns.
The default status code for redirects are 301 redirects which are PERMANENT redirects.
You can also pass a closure as the target
of relocation. This closure will received the parsed parameters, the incoming route record and the event object. You can determine dynamically where the relocation will go.
This is great if you need to actually parse the incoming route and do a dynamic relocation.
Happy Redirecting!
You can also redirect a pattern to a handler using the toHandler()
method. This is usually done if you have the action coming in via the URL or you are using RESTFul actions.
You can also route a pattern to HTTP RESTFul actions. This means that you can split the routing pattern according to incoming HTTP Verb. You will use a modifier withAction()
and then assign it to a handler via the toHandler()
method.
The Router allows you to create inline responses via closures/lambdas or enhanced strings to incoming URL patterns. You do not need to create handler/actions, you can put the actions inline as responses.
If you use a response closure/lambda, they each accepts three arguments:
event
- An object that models and is used to work with the current request (Request Context)
rc
- A struct that contains both URL/FORM
variables merged together (unsafe data)
prc
- A secondary struct that is private only settable from within your application (safe data)
If the response is an HTML string, then you can do {rc_var}
replacements on the strings as well:
You can also register routes that will respond to sub-domains and even capture portions of the sub-domain for multi-tenant applications or SaaS applications. You will do this using the withDomain()
method.
You can leverage the full routing DSL as long as you add the withDomain()
call with the domain you want to bind the route to. Also note that the domain string can contain placeholders which will be translated to RC
variables for you if matched.
Tip: Please note that you can leverage Routing Groups as well for domains
You can also add variables to the RC and PRC structs on a per-route basis by leveraging the following methods:
rc( name, value, overwrite=true )
- Add an RC
value if the route matched
rcAppend map, overwrite=true )
- Add multiple values to the RC
collection if the route matched
prc( name, value, overwrite=true )
- Add an PRC
value if the route matched
prcAppend map, overwrite=true )
- Add multiple values to the PRC
collection if the route matched
This is a great way to manually set variables in the incoming structures:
You can also apply runtime conditions to a route in order for it to be matched. This means that if the route matches the URL pattern then we will execute a closure/lambda to make sure that it meets the runtime conditions. We will do this with the withCondition(
) method.
Let's say you only want to fire some routes if they are using Firefox, or a user is logged in, or whatever.
You can create a-la-carte namespaces for URL routes. Namespaces are cool groupings of routes according to a specific URL entry point. So you can say that all URLs that start with /testing
will be found in the testing namespace and it will iterate through the namespace routes until it matches one of them.
Much how modules work, where you have a module entry point, you can create virtual entry point to ANY route by namespacing it. This route can be a module a non-module, package, or whatever you like. You start off by registering the namespace using the addNamespace( pattern, namespace )
method or the fluent route().toNamespaceRouting()
method.
Once you declare the namespace you can use the grouping functionality to declare all the namespace routes or you can use a route().withNamespace()
combination.
Hint You can also register multiple URL patterns that point to the same namespace
You can register routes in ColdBox with a human friendly name so you can reference them later for link generation and more.
You will do this in two forms:
Using the route()
method and the name
argument
Using the as()
method
If you do not pass the name
argument to the route()
method, we will use the pattern
as the name of the route.
You will generate URLs to named routes by leveraging the route()
method in the request context object (event).
Let's say you register the following named routes:
Then we can create routing URLs to them easily with the event.route()
method:
The request context object (event) also has some handy methods to tell you the name or even the current route that was selected for execution:
getCurrentRouteName()
- Gives you the name of the current route, if any
getCurrentRoute()
- Gives you the currently executed route
getCurrentRoutedURL()
- Gives you the complete routed URL pattern that matched the route
getCurrentRoutedNamespace()
- Gives you the current routed namespace, if any
There will be a time where your routes will become very verbose and you would like to group them into logical declarations. These groupings can also help you prefixes repetitive patterns in many routes with a single declarative construct. These needs are met with the group()
method in the router.
The best way to see how it works is by example:
As you can see from the routes above, we have lots of repetitive code that we can clean out. So let's look at the same routes but using some nice grouping action.
The options struct can contain any values that you can use within the closure. Grouping can also be very nice when creating namespaces, which is our next section.
Resourceful routes are convention based to help you create routing with less boilerplate.
In ColdBox, you can register resourceful routes (resources()
) to provide automatic mappings between HTTP verbs and URLs to event handlers and actions by convention. By convention, all resources map to a handler with the same name or they can be customized if needed. This allows for a standardized convention when building routed applications and less typing
This single resource declaration will create all the necessary variations of URL patterns and HTTP Verbs to actions to handle the resource. Please see the table below with all the permutations it will create for you.
Verb | Route | Event | Purpose |
---|---|---|---|
For in-depth usage of the resources()
method, let's investigate the API Signature:
We have created a scaffolding command in CommandBox to help you register and generate resourceful routes. Just run the following command in CommandBox to get all the help you will need in generating resources:
If you are building mostly API routes and not full HTML app routes, you can use the shortcut method apiResources()
method instead. This method will work the same as above BUT it will exclude the new
and edit
actions for you since we are in API Land.
Verb | Route | Event | Purpose |
---|---|---|---|
GET
/photos
photos.index
Get all photos
GET
/photos/new
photos.new
Return the HTML form for creating a photo
POST
/photos
photos.create
Create a photo
GET
/photos/:id
photos.show
Show a photo by id
GET
/photos/:id/edit
photos.edit
Return the HTML form for editing the photo
PUT/PATCH
/photos/:id
photos.update
Update a photo by id
DELETE
/photos/:id
photos.delete
Delete a photo by id
GET
/photos
photos.index
Get all photos
POST
/photos
photos.create
Create a photo
GET
/photos/:id
photos.show
Show a photo by id
PUT/PATCH
/photos/:id
photos.update
Update a photo by id
DELETE
/photos/:id
photos.delete
Delete a photo by id