ColdCourse: Search Engine Safe URLs for Fusebox, Mach-II, Model-Glue and ColdBox
One thing that’s always been left out of Coldfusion frameworks (with the exception of Coldfusion On Wheels) is URL handling. This is a big thing. It’s not essential for making an application, and it certainly can be a time drain to get URLs just right. Ruby on Rails started a trend in controlling URLs not through URL rewriting, but more tightly bundled into the framework. Coldfusion on Wheels did this too, although it didn’t get that much attention for what was one of the most amazing innovation of any CF framework I’d seen in a while.
So what is ColdCourse? ColdCourse is a framework agnostic routing system that will execute before the framework. You know that index.cfm file that you’re never supposed to touch? Well this is a modification to that. Before the request hits your framework, ColdCourse will translate the URL into the URL variables you’re expecting, and set them back to the URL scope. Here’s an example:
http://localhost/index.cfm?fuseaction=home.main
Would be the same as…
http://localhost/home/main
In the configuration file for ColdCourse you setup a few things to identify the framework you’re using, such as what the event variable should be (fuseaction, event), what the separator should be (it’s always a period for fusebox and coldbox, but you could be using anything for Mach-II or Model-Glue), what the default action should be if you don’t include an action and more.
You’re probably thinking that with Mach-II and Model-Glue you really don’t have to have URLs that are in the form “controller.action”. Well you’re right. In order to use ColdCourse though, you’ll need to follow a convention where every single event in your framework has exactly one controller and exactly one action. I think you could also make it work if you have all single world actions as well by setting the separator to “” and the action to “”, but I have not yet tested that out, and you would be unable to use ANY controller/action pairs.
Default Actions
Don’t let all this controller/action talk make you think that all URLs must contain both. There are quite a few ways to tweak the URLs to do what you want. The easiest way is through the FrameworkActionDefault set in the config file. Lets say you default this to “index”, then when you go a URL that only sets the controller, this will be automatically set as the action. For instance:
http://localhost/home/index would be the same as http://localhost/home.
Unique URLs
With all these different URLs flying around there’s a chance a search engine might pick up both those, or even worse — 3! You probably don’t want Google deciding which of these URLs is your REAL URL, so ColdCourse handles that. For instance, if someone goes to that initial URL we mentioned, http://localhost/index.cfm?fuseaction=home.main, ColdCourse will issue a 301, permanently moved header response and using a header location redirect them to http://localhost/home/main. Also, if a user goes to http://localhost/home/index they will get redirected to http://localhost/home in the same way.
Gotchas
Nothing is perfect! This is the first attempt at this, and although I know the CF On Wheels portion to be tested by quite a few people, my code has not been. As far as implementing this in your own project, there are a few things to watch out for. If you’re using any kind of “self” or “myself” variable, chances are it has the framework specific values in there. These can be updated in a prefuseaction or an onrequeststart method in your framework and set to “” in order to make things easier. If you want to test things out NOW without changing much code, you might want to change whatever variable contains “index.cfm” and change it to “/index.cfm”. Those paths really start to get hairy if you’re trying to do a little in both worlds though, so I’d recommend either going all out or none at all. You’re probably setting your XFAs as “controller.action” as well too, which may need to be refactored into “/controller/action/”. URL variables and FORM variables passed to non-Coldcourse URLs will be forwarded over to the Coldcourse URL. For forms this a VERY bad idea if you’re doing anything sensitive, and just a BAD idea if you’re not. It’ll also mess things up if you need to know what’s in the form scope and url scope. I think all frameworks covered will just squish these into a single scope though, so although the end result is the same, be sure to fix forms to go to the new URLs if you do nothing else.
What you need
Just like the routing in COW, you’ll need either a webhost that supports .htaccess files (apache), or access to IIS to install an Isapi filter. Other than that you really only need to be able to run CFCs. This hasn’t been tested on other versions besides CF7, but should work in theory on any 6.1+ style CFML processor.
Installation
Installation is a two second process if you’re on apache, but will be a little more trouble if you’re running IIS. For IIS, you’ll have to get Isapi Rewrite working and include the packaged ini file. For Apache you just drop the .htaccess file in your root dir. Other than that just place your config file, Coldcourse.cfc and coldcourse.cfm wherever you want. By default these will go in /config, /model and /, respectively, but can go anywhere if you change their paths in coldcourse.cfm. Next you can setup your framework specific settings and some courses in the config file.
Credits
To be honest, all the hard work for this came from the CF On Wheels guys, Rob Cameron & Per Djurner for making the routing system used there. This is just an adaptation of their code to work with other frameworks.
Download
You can grab it now on Riaforge at http://coldcourse.riaforge.org/.
If you enjoyed this post, please consider to leave a comment or subscribe to the feed and get future articles delivered to your feed reader.




Comments
Thanks! I actually started it saturday morning, and it took about an hour and a half to have a working version since most the work was in cfwheels.
@Geoff
Ahh, very true. Thanks for the heads up.
It does do full URL rewriting assuming you meet the requirements (apache with it enabled or IIS with it installed). It uses .htaccess files for URL rewriting in apache, and IsapiRewrite4.ini to handle the rewrite rules in IIS. At the moment that’s all that’s supported. Here’s the entire code snippet from it:
IsapiRewrite.ini
IterationLimit 0
RewriteRule ^(/.+/.+/.*\?.+\..*)$ /index.cfm?course=$1
RewriteRule ^(/[^.]*)$ /index.cfm?course=$1
.htaccess
RewriteEngine on
RewriteRule ^$ index.cfm [QSA]
RewriteRule ^([^.]+)$ $1.htm [QSA]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.cfm?course=%{REQUEST_URI} [QSA,L]
You could technically use Coldcourse without a webserver that supports URL rewriting as well. In which case instead of your URL lookiking like:
http://www.mysite.com/controller/action
it would be
http://www.mysite.com/ndex.cfm?course=/controller/action
Which both mean the same thing. There’s a setting in the Coldcourse config file that will also redirect (permanently moved) the “http://www.mysite.com/ndex.cfm?course=/controller/action” link to “http://www.mysite.com/controller/action” if you want, which would be disabled if you’re not using the URL rewriting feature.
Thanks for the response. I am wondering why you chose to use url rewriting since you start this post by mentioning Rail’s boast that it does not use url rewriting for url handling and neither does ColdFusion on Wheels?
I don’t think RoR has SES urls, but couldn’t the same technique used in ColdFusion on Wheels for url handling be implemented to make SES urls?
Rails and Coldcourse handle URLs in the same way. Both of them rely on URL rewriting AND a special script on the server to handle that specially modified URL. The point of it that you as a programmer should never have to modify the “URL rewrite” rules, so you’ll never have to know .htaccess or isapi filter regular expressions. Instead you’ll only write little lines of code that further extend that. For instance, the basic one that’s built in is:
<cfset addcourse(”/:controller/:action/:id”)/>
What that means is that when the user goes to the URL
http://localhost/mycontroller/myaction/someid
the URL rewriting reads that in and the actual page that’s called is
http://localhost/index.cfm?course=/mycontroller/myaction/someid
Coldcourse matches up that “/mycontroller/myaction/someid” against the possible rules declared in the coldcourse.cfm config file and see that it matches the format of the one listed above. The actual page that would be called would be equal to something more like
http://localhost/index.cfm?fuseaction=mycontroller.myaction&id=someid
Assuming you’d setup the configuration file to use fusebox. All the user ever sees is “http://localhost/mycontroller/myaction/someid” as the URL, but behind the scenes the .htaccess/isapi filter converts that URL to something coldcourse can use, then coldcourse converts it to something your framework can use.
RoR does have SES URLs, it’s the same practice as this in how you add them as well. In Rails they’re called “routes”, and are configured using the same syntax Rob developed for CF Wheels. All of them use URL rewrite, the beauty of it though is that you never have to mess with things at the regular expression level, you can modify what variables get set and how it comes over.
Here’s another quick example:
<cfset addcourse(pattern=”/validate/:model” ,controller=”check” )/>
Now if you hit the URL http://localhost/validate/anythingHere
Then that would be the same as going to http://localhost/index.cfm?fuseaction=check.index&model=anythingHere
Items in the pattern that look like “:model” are converted to “model=value” in the URL, while if they are left off, as with “validate”, that string must exist in the URL for it to be hit.
A better example for this might be if you wanted all your services to be available at http://localhost/services, then you could add a rule for it to be used from there.
Hope that helps it make a little more sense!
If you want to add variables (url) then you put in the standard…
?variable=value&anotherVar=2
…and it works.
So, what is the difference. (NO APACHE CONFIG REQUIRED.) It may now work one every platform, but certainly if you require Apache then the same statement is true of this solution.
http://localhost/index.cfm/user/AdamFortuna ?
That’s certainly a good option. I expect for search engines both would be about the same, so anything past that is personal preference. I like being able to remember the URLs and tell them to other people who might not remember “index.cfm”, with the added bonus of being a slightly shorter URL as well.
That explanation is very helpful. I am really excited about ColdCourse now. The best part is that it is a separate framework all of its own!
http://www.micronovae.com/ModRewrite/ModRerite.html
i have tried to use this with CF6 and IIS (Isapi installed), but if i insert the line in index.cfm
i get following error: Element COLDCOURSE is undefined in a Java object of type class [Ljava.lang.String; referenced as
Does anybody know, why i get this errror?
Also from the examples above, the same would seem true for possible url variables needing to be defined within the “Custom Courses”.
Actually you don’t need to define any fuseactions and circuits with the addcourse function, except the one default. The idea is that it’ll parse out the current fuseaction and use the first part of it as the controller and the second as the action. That way any request in the form:
http://localhost/home/main
would be the same as
http://localhost/?fuseaction=home.main
Assuming you keep the default rule in the form (/:controller/:action).
First I want to say thanks to Adam Fortuna for this wonderful product. But I can’t get it working.
I’m using fusebox 5.5 and have isapi rewrite support installed. (VISTA)
There’s a bug in the model/Coldcouse.cfc, the include template in line 15 is not defined. So, I changed this to
When I try to run a fusebox application with ColdCourse, I get the following error: undefined Circuit - You specified a Circuit of site_name.index which is not defined.
So, how to define the default fuseaction in coldcourse.config.cfm?
As I have understand you only need to define the default fuseaction and the ColdCourse will automatically use the use the first part of it as the controller and the second as the action.
So I don’t need to set up all the addCourse for all my links on the site.
Hope anyone can help.
Best regards
Rune
Looks like it cut off the part you replaced, mind posting it again without the tags?
You don’t need to define a default action in coldcourse, you’ll set your usual default in fusebox and that’ll be used. By default the fuseaction variable will be undefined, so the framework itself will set it. But you’re right, you don’t need to use addCourse for all your links. The default path will be /:controller/:action, with the circuit replacing controller and the fuse replacing action, so going to fuseaction=home.main will be the same as /home/main. You don’t need to actually define home.main in here because any path that comes in with the path /anything/anythingelse will go to fuseaction=anything.anythingelse.
Fist about the error I get when I install ColdCourse.
Directory structure:
Config and Model and application.cfc and index.cfc on the root.
Gives the following error: Could not find the included template /config/coldcourse.config.cfm.
So, I changed line 15 to
Here is my index.cfm file
As u can see I call the application cc (not for Credit Card, but for ColdCourse).
Some code from Fusebox.xml
I’m testing this on Vista and here are the Rewrite rules:
# Helicon ISAPI_Rewrite configuration file
# Version 3.1.0.34
RewriteRule ^(/.+/.+/.*\?.+\..*)$ /index.cfm?course=$1
RewriteRule ^(/[^.]*)$ /index.cfm?course=$1
Setup in coldcourse.config.cfm
Ok, lets try this configuration:
This will give an error:
You specified a Circuit of cc.index which is not defined.
Then lets change setFrameworkActionDefault to index.cfm
This will give the following error: File not found: /index.cfm
The error will be the same if commenting out the fusebox call in index.cfm
But if keeping the fusebox call and commenting out the coldcourse call in index.cfm, the application works, now as a normal fusebox application.
Well, this was some of my settings and testing with coldcourse.
And I really hope someone can explain what I’m doing wrong. I really like to remove the index.cfm?fuseaction= from my links.
Best regards
Rune
Regards
Rune
Thanks for the questions and all the info. Definitely seeing where I need to improve in terms of documentation — as there isn’t much on any of this.
I’d try putting the application.coldcourse call right before the cfinclude call — after your cfapplication call. That might be causing some problems.
Also, how are you instantiating that application.coldcourse variable? You might want to do that either on startup or in your application.cfc file:
http://svn.riaforge.org/coldcourse/trunk/Application.cfc
This startup has to be done prior to fusebox though unfortunately. Because of this some people using application.cfm have set a dev/prod flag in their application.cfm and if it’s dev or undefined always reload coldcourse.cfc, and if it’s prod use what’s already loaded.
Also, i think my documentation on setFrameworkActionDefault() is a bit shoddy. This is not a file on the filesystem but a convention you can use within your ap for URLs.
For instance, if you want to go to the URL http://localhost/home, and had the frameworkActionDefault set to “index” this would be the same as going to http://localhost?fuseaction=home.index. If frameworkActionDefault was set to welcome, this would be like going to http://localhost?fuseaction=home.welcome. Basically it’s the default fuse to call within a circuit if none is provided.
Want to try these changes and see if you have any luck first? Might work out better after that.
Thanks for your answer again. I’m now testing out what u has written – but it looks like it doesn’t work, anyway hope I soon get it to work.
I have also created an email for this on one of my domains.
Can you email me at coldcourse@scandicweb.com and I will send you the source code for this coldcourse test using fusebox.
Best regards
Rune
I’ve tried making coldcourse work with fusebox 5.5.1 and I’ve failed even though I did everything step by step what was written in the readme file.
I decided to look into the code to find out what exactly is happening there and it seems that at the end coldcourse just sets proper keys in the URL struct. This could make sense if all that happened before fusebox has started, but how can that be achieved? First file processed is the Application.cfc which extends fusebox’s application.cfc - and thus fusebox is processed as the first, am I wrong?
You have recommended to run coldcourse.dispatch in index.cfm but when using fusebox index.cfm is ignored… isn’t it?
Sorry for my english but it’s not my native language.
Chris T
Leave a comment