Raymond Camden's Blog Rss

onMissingTemplate Example

39

Posted in ColdFusion | Posted on 07-20-2007 | 7,554 views

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:

view plain print about
1<cffunction name="onMissingTemplate" returnType="boolean" output="false">
2    <cfargument name="thePage" type="string" required="true">
3    <cflog file="cfblgogersmissingfiles" text="#arguments.thePage#">
4    <cflocation url="404.cfm?thepage=#urlEncodedFormat(arguments.thePage)#" addToken="false">
5</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):

view plain print about
1<cfparam name="url.thePage" default="">
2
3<cfif not len(trim(url.thePage))>
4    <cflocation url="index.cfm" addToken="false">
5</cfif>
6
7<cf_layout title="File Not Found">
8
9<h2>These are not the droids you are looking for...</h2>
10
11<p>
12Sorry, but the page you requested, <cfoutput>#url.thePage#</cfoutput>, was not
13found on this site.
14</p>
15
16</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

[Add Comment] [Subscribe to Comments]

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

It bothers me that this only works for .CFM pages, but still, very cool.
Did you hear Paris Hilton started a new web site after getting out of Jail?

It's called Parisite.com.
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.
"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...
@duncan: Zing!
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.
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/
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.)
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> ?? ;-)
You can't add the text, just the code.
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. ;)
ColdFusion 404 handlers are only half the battle:

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

Nice default IIS page.
You guys got too much time on your hands. ;)
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...
I think you might be right. I'll see about updating the code this week.
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.
Wow, I tend to be pretty anal about that. Good catch. It is fixed.
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>
Edward - are you asking how it looks in general? It seems fine to me. Not sure why you have a try catch though.
"Not sure why you have a try catch though ..."

I like pointless code :->
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?
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.
I shall be heading over to Amazon now ...
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
Thanks Ray!

One step forward ...
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().
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.
Thanks Ray - sorry for the cross-post/comment. Agree on the endless loop. My testing seemed fine.
First time poster, long time reader ;-)

A recommended change would be to add a proceeding slash to the URL location:

<cflocation url="/404.cfm?thepage=#urlEncodedFormat(arguments.thePage)#" addToken="false">

So should someone really go wrong and type in "www.foo.com/folder/what/huh.cfm";; we'll redirect to the root file.

Thanks for your knowledge sharing, you've helped me out of a few jams in the past!

Phive.
Interesting thing I discovered today on this issue: If you don't use <cfinclude> or <cflocation> and have the onMissingTemplate method return false, CF will default to the 404 page you defined in the CFIDE administrator settings. Returning false means that the correct status of 404 will be sent in the http response headers.

Thus my onMissingTemplate simply has a <cflog> followed by <cfreturn false />. No more, no less.
How do you guys know which status code is being returned by the server?

I want to implement OnMissingTemplate(), but I don't want to return a 404. It looks like cfheader will take care of the status code for me, but I need to be able to test that it's working somehow.
Have you tested yourself - to see what the status is?
Never mind.

The Web Developer plug-in for Firefox has an option to View Response Headers under the Information menu. My quick test yields a "200 OK", which is pretty much what I was expecting. Thanks.
Little late to the party here but shouldn't you be sending a "404 not found" via cfheader before you cfinclude your missing template handler..? That way you avoid getting "200 OK" for a non-existent page... no?
Sure - that would make sense.
How can I get my onMissingTemplate handler to catch Page Not Found errors that occur when the query string begins with an ampersand:

http://mysite.com/index.cfm&x=1

Even with a working onMissingTemplate handler, these are not caught. Is a Custom Error Handler in IIS the only solution?
That's not a valid URL. URLs must have ? in them before anything after the file name.
Ok, I just thought there might be a way to catch it, since it throws a Page Not Found exception. Thanks.
It might be web server dependent.

[Add Comment] [Subscribe to Comments]