onMissingTemplate Example

I just added onMissingTemplate support to ColdFusionBloggers.org. This is something we should all do with ColdFusion 8 sites as it is so simple it doesn't make sense not to. To test, simply visit:

http://www.coldfusionbloggers.org/parishiltonisfetch.cfm

Here is what I added to my Application.cfc file:

<cffunction name="onMissingTemplate" returnType="boolean" output="false">
   <cfargument name="thePage" type="string" required="true">
   <cflog file="cfblgogersmissingfiles" text="#arguments.thePage#">
   <cflocation url="404.cfm?thepage=#urlEncodedFormat(arguments.thePage)#" addToken="false">
</cffunction>

The first thing I do is log the request. As I've mentioned before, logging the 404 can be handy as you may see people requesting the same file again and again. It may be worthwhile to add that page and put a redirect there or some other content. I then cflocate to a handler. My handler is rather simple (I've trimmed out some of the silly text):

<cfparam name="url.thePage" default="">

<cfif not len(trim(url.thePage))>
   <cflocation url="index.cfm" addToken="false">
</cfif>

<cf_layout title="File Not Found">

<h2>These are not the droids you are looking for...</h2>

<p>
Sorry, but the page you requested, <cfoutput>#url.thePage#</cfoutput>, was not
found on this site.
</p>

</cf_layout>

As you can see, I check for the existence of the URL variable (in case people visit the 404 page directly) and print out a message telling the user that their file didn't exist.

I've updated the code zip on ColdFusionBloggers.org. It now contains this change and the "auto refreshing div" modification I made yesterday.

Comments

Nice! That's like some crazy Jedi mind tricks :)

It bothers me that this only works for .CFM pages, but still, very cool.
# Posted By Ben Nadel | 7/20/07 8:28 AM
Did you hear Paris Hilton started a new web site after getting out of Jail?

It's called Parisite.com.
# Posted By Dan G. Switzer, II | 7/20/07 8:33 AM
It is amazing how many sites don't provide for a missing template handler or site wide errorhandling, as Ray says it isn't a big effort to include either, it's just plain laziness.

I have see quite a few websites that display ugly extensive errors when something a bit more end-user friendly should be displayed.
# Posted By Gary Gilbert | 7/20/07 9:04 AM
"It is amazing how many sites don't provide for a missing template handler or site wide errorhandling, as Ray says it isn't a big effort to include either, it's just plain laziness."

er,
http://www.garyrgilbert.com/parishiltonisfetch.cfm...
http://www.coldfusionjedi.com/parishiltonisfetch.c...
# Posted By duncan | 7/20/07 9:22 AM
@duncan: Zing!
# Posted By Todd Rafferty | 7/20/07 9:57 AM
Heh, I'm as perfect as anyone else. As for my blog - it uses App.cfm, not App.cfc. BlogCFC 6 may be supporting both though.
# Posted By Raymond Camden | 7/20/07 10:13 AM
Hi Ray,

You've got a 302 redirect in there still...
So you're saying "The parishiltonfetch.cfm page *does* exist, but is temporarily elsewhere" then you get the 404 for the "elsewhere" page...

Can you make the cflocation into a 301 redirect?

Or better still, can you not cfinclude your 404.cfm page directly from within onMissingTemplate? That way you'd avoid the "302 moved temporarily" redirect? - the 404 would then apply to "parishiltonfetch.cfm" not to "404.cfm?thepage=%2Fparishiltonfetch%2Ecfm"

Try this:
http://www.seoconsultants.com/tools/headers/
# Posted By Geoff | 7/20/07 1:02 PM
Yep. ColdFusion 8 added the ability to add status headers to cflocation. I will do that right now. (But zip will be a bit later this week.)
# Posted By Raymond Camden | 7/20/07 1:10 PM
Sorry about this Ray - I reckon you need another change:

<cfif not len(trim(url.thePage))>
<cflocation url="index.cfm" addToken="false">
</cfif>

should be:

<cfif not len(trim(url.thePage))>
<cfheader statuscode="404" statustext="Not Found">
<cfabort>
</cfif>

Just my 2 cents - can you tell I hate <cflocation> ?? ;-)
# Posted By Geoff | 7/20/07 1:34 PM
You can't add the text, just the code.
# Posted By Raymond Camden | 7/20/07 1:36 PM
Oh wait - you mean on the 404 page. That cflocation was meant to just stop people from hitting 404 directly. I think I'm ok with that. ;)
# Posted By Raymond Camden | 7/20/07 1:36 PM
ColdFusion 404 handlers are only half the battle:

http://www.coldfusionbloggers.org/parishiltonisfet...

Nice default IIS page.
# Posted By errorik | 7/20/07 4:19 PM
You guys got too much time on your hands. ;)
# Posted By Raymond Camden | 7/20/07 4:26 PM
I've been playing with onMissingTemplate, and I reckon this would be better:

<cffunction name="onMissingTemplate" returnType="boolean" output="true">
<cfargument name="thePage" type="string" required="true" />
<cfset somevariable=thepageyoutriedtoget />
<cfinclude template="404.cfm" />
<cfreturn true />
</cffunction>

Using this cfinclude method, I'm not redirecting (302 or 301) then serving a 404, hence the 404 will apply to the page you tried to get, not to the 404.cfm page itself...
# Posted By Geoff | 7/23/07 8:35 AM
I think you might be right. I'll see about updating the code this week.
# Posted By Raymond Camden | 7/23/07 8:39 AM
xss-able.

http://www.coldfusionbloggers.org/404.cfm?thepage=';alert(String.fromCharCode(88,83,83))//\';alert(String.fromCharCode(88,83,83))//%22;alert(String.fromCharCode(88,83,83))//\%22;alert(String.fromCharCode(88,83,83))//--%3E%3C/SCRIPT%3E%22%3E'%3E%3CSCRIPT%3Ealert(String.fromCharCode(88,83,83))%3C/SCRIPT%3E

Not a big problem, but should be fixed.
# Posted By Ben | 1/2/08 3:34 PM
Wow, I tend to be pretty anal about that. Good catch. It is fixed.
# Posted By Raymond Camden | 1/2/08 3:38 PM
Ray ... how's this look ...

<cffunction name="onMissingTemplate" returnType="boolean" output="true">
<cfargument name="targetPage" type="string" required="true" />
<cftry>
<cflog type="error" text="Missing template: #Arguments.targetPage#">
<cfinclude template="404.cfm" />
<cfreturn true />
<cfcatch>
<cfreturn false />
</cfcatch>
</cftry>
</cffunction>
</cfcomponent>
# Posted By Edward Beckett | 4/13/08 5:27 PM
Edward - are you asking how it looks in general? It seems fine to me. Not sure why you have a try catch though.
# Posted By Raymond Camden | 4/14/08 9:23 AM
"Not sure why you have a try catch though ..."

I like pointless code :->
# Posted By Edward Beckett | 4/14/08 11:09 AM
Okay ... now another problem exists ....

For some reason I am getting an error thrown by the onMissingTemplate function for my 404 page ... request was not of type boolean ... very strange ... here's what I came up with as a slight change to Geoff's code ...

<cffunction name="onMissingTemplate" returnType="void" output="true">
<cfargument name="targetPage" type="string" required="true" />
    <cflog type="error" text="Missing template: #Arguments.targetPage#">
<cfinclude template="404.cfm" />
</cffunction>

However, when I changed the retrunType to void ... there is no problem ...

Any reason why an error would be thrown from the return type being set as boolean?
# Posted By Edward Beckett | 4/19/08 7:06 AM
Well sure, Edward. If you ahd returnType="boolean" and you didn't do cfreturn true, then this error would occur. Your returntype has to match with how you end the func - whether it is App.cfc, any other CFC, or a tag based UDF.
# Posted By Raymond Camden | 4/19/08 7:20 AM
I shall be heading over to Amazon now ...
# Posted By Edward Beckett | 4/19/08 9:09 AM
Specifically on App.cfc, there are rules for what you should return. Look at my app.cfc template on the left. These rules apply to _how_ your app acts based on the return. So for example, if you return false in onRequestStart, you are basically aborting the request (I think it throws an error too - not sure).

So to be clear you have 2 things here:

a) First is the returnType must match rule, which applies _everywhere_
b) Second is the application behavior depeneding on what you do in the method
# Posted By Raymond Camden | 4/19/08 9:23 AM
Thanks Ray!

One step forward ...
# Posted By Edward Beckett | 4/19/08 9:43 AM
Just a note about earlier code that used cflocation that the CF8 documentation states specifically that: To include the contents of a page in the onMissingTemplate function, use the cfinclude tag. Do not use any other method to include or redirect other page content, including tags and functions such as cflocation,
GetPageContext().forward(), and GetPageContext().include().
# Posted By Johan | 4/26/08 6:39 PM
Thats interesting. As I said in reply to your other comment, I think this may be a mistake. The only reason I can see against doing a cflocate is to prevent accidental infinite looping by cflocating to a page that doesn't exist.
# Posted By Raymond Camden | 4/27/08 8:03 AM
Thanks Ray - sorry for the cross-post/comment. Agree on the endless loop. My testing seemed fine.
# Posted By Johan | 4/27/08 2:56 PM