Are You Using onRequest in Application.cfc?

Posted By : todd sharp Posted At : May 16, 2007 7:51 AM Posted In: ColdFusion

11

I'm a really big fan of application.cfc. I like the organization and the fact that I can define certain events and variables based on the various events that happen when my application is used.

To me it's much cleaner and organized to to do this:

<cfcomponent>
<cffunction name="onApplicationStart">
<cfset application.foo = "something">
</cffunction>
</cfcomponent>

Then this:

<cfif not structKeyExists(application, "foo")>
<cfset application.foo = "something">
</cfif>

Just knowing that foo will only be set if my application starts and that I don't have to worry about whether or not it may already exist makes me happy.

That being said I realized recently that I personally have not been taking full advantage of much of the power of the various functions available. For example, I often take advantage of onRequestStart to do user authentication, page logging, etc - but I rarely use onRequest. So what is a good use case for onRequest? Well lets start with a simple skeleton function showing the structure of onRequest. This is copied from Ray Camden's Application.cfc reference which I highly recommend you get a copy of and use as a skeleton for your applications that use application.cfc.

<cffunction name="onRequest" returnType="void">
<cfargument name="thePage" type="string" required="true">
<cfinclude template="#arguments.thePage#">
</cffunction>

Pretty simple eh? The function accepts one argument - the page being requested. So what else can you do in onRequest? Well notice that the page that is passed is then included back into the function. How about doing something before that request? Like what? Well setting variables is a possibility - but may be unlikely. Application wide variables should be set in onApplicationStart. Variables specific to a template would be better off being set in the template itself to avoid confusion and hard to read code. What we could do is repetitive tasks that we'd rather not even care about seeing happen on an individual template basis. Like what? Well lately I've been using onRequest to parse search engine safe (SES) URL's. I will normally have a component of utils/udf's that I create in the application scope. One of the functions may be something like this parseSES function (borrowed once again from Ray).

function parseSES() {
var pathInfo = reReplaceNoCase(trim(cgi.path_info), '.+\.cfm/? *', '');
var i = 1;
var lastKey = "";
var value = "";

if(not len(pathInfo)) return;

for(i=1; i lte listLen(pathInfo, "/"); i=i+1) {
value = listGetAt(pathInfo, i, "/");
if(i mod 2 is 0) url[lastKey] = value;
else lastKey = value;
}
//did we end with a "dangler?"
if((i-1) mod 2 is 1) url[lastKey] = "";
return;

}

So my onRequest would look something like this (assuming the function above was included in a component created in the application scope called utils).

<cffunction name="onRequest" returnType="void">
<cfargument name="thePage" type="string" required="true">
<cfset application.utils.parseSES()>
<cfinclude template="#arguments.thePage#">
</cffunction>

Simple but effective. Now I do not have to worry about calling that function on every template. It's just done. I can now assume that every template will correctly interpret a URL like http://foooooo.com/todd/sharp as url.todd = 'sharp'. You could also take advantage of onRequest to cfinclude a UDF library, etc.

Comments (11)

Ben Nadel's Gravatar That's a pretty cool idea. To take that even one step further, what would be totally sweet is if ColdFusion could handle 404 errors a bit better and then we could use OnRequest() to re-write URLs... very similar to what you are doing, except that your page exists, where as a fake, SEO page might not.

Sean Corfield's Gravatar Don't forget that onRequest() breaks web service / Flash Remoting calls to CFCs. See the Application.cfc documentation for more details on why.

Ben Nadel's Gravatar Sean is correct, it will break. A long while back, Ray Camden posted a simple solution for this, which was simply to delete the OnRequest() application event if a web service was being called.

I tried to find Ray's original post but I cannot. At the very least, here is a link to a small entry on how I applied Ray's suggestions:

http://www.bennadel.com/blog/430-My-First-ColdFusi...

todd sharp's Gravatar Thanks Sean/Ben - I actually did not know that. It makes sense though...

Typically my CFCs are in a seperate directory with it's own app.cfc that is usually pretty bare bones. I may also have a app.cfc that extends the base app.cfc and overrides potential permissions set in onRequestStart() (Sean I think you actually are the one that turned me on to that) but I don't really see the need to ever consider onRequest in those.

I suppose that brings up a good point though - if you are extending a base app.cfc and it uses onRequest you'll potentially have a problem...

Ben Nadel's Gravatar Ha ha ha ha... sorry, I didn't mean any disrespect by not giving you credit :) Small world.

Sean Corfield's Gravatar NP. I just think it's funny that my 'hack' might become a 'standard practice' :)

Ben Nadel's Gravatar I think it's quite an elegant solution. And, frankly, I think it's really cool that you can remove application-level events at page run time. Cool!

todd sharp's Gravatar I wouldn't consider it a 'hack' to have the cfc in a separate directory without an onRequest...

Sean Corfield's Gravatar @Todd, no I wouldn't consider that a hack either. My hack was deleting onRequest from the CFC on the fly inside onRequestStart.

Rick Hopper's Gravatar I'm new to using Application.cfc, and I have a question. Is there a difference between putting code in onRequestStart vs. onRequest before the template include? I've put the parseSES() function call in onRequestStart(), and I didn't know if it mattered one way or another.

@Todd. Also, your example for parseSES() was http://foooooo.com/todd/sharp. Does that work without including the filename? When I try that, I get a 404 error, b/c it assumes /todd/sharp is part of the directory path. I was assuming I'd have to work with 404 error handling to be able to leave off the filename, but I wasn't sure if there was a trick to getting it to work with parseSES().