One of the more interesting new features in ColdFusion 8 is an update to the Application.cfc file. You can now add a method called: onMissingTemplate. As you can imagine, it fires off when a request comes in for a CFM file that doesn't exist.
Take a look at the basic method signature:
<cffunction name="onMissingTemplate" returnType="boolean" output="false">
<cfargument name="targetpage" type="string" required="true">
</cffunction>
As you can see - it has a boolean return type and one argument - the name of the page that had attempted to load. Here is a simple example:
<cffunction name="onMissingTemplate" returnType="boolean" output="false">
<cfargument name="targetpage" type="string" required="true">
<!--- log it --->
<cflog file="missingfiles" text="#arguments.targetpage#">
<cflocation url="/apptest/404.cfm?f=#urlEncodedFormat(arguments.targetpage)#" addToken="false">
</cffunction>
In this case, I log the file to a log named missingfiles. I then send the user to a page and pass along the file she tried to load. On 404.cfm, all I do is tell the user their file didn't exist and I direct them to the home page. You could handle this anyway you like of course. For example - why not just log it and then send the user to the home page? That is acceptable as well. Although personally I like to at least be told I tried to load a file that didn't exist.
It is certainly nice to have this added to ColdFusion, but there are a few caveats you need to be aware of.
First - it is only going to fire off for CFM requests. So if you try to go to:
www.dharma.com/foo.cfm
And foo doesn't exist, it will fire. If you try to go to:
www.dhamra.com/foo.html
then your web server will handle it. The same applies for missing directories. Consider this example:
www.dhamra.com/secretfolder
This will not fire onMissingTemplate.
But you can handle a "deep" directory if the end of the request is a CFM file. This will work fine:
www.dhamra.com/2007/12/2/myarticle.cfm
Another thing to watch out for is that onMissingTemplate will not run onError. Therefore - simply write your code perfectly. (Ok, that is a joke.) You can wrap your code in try/catch to be extra careful.
You also need to watch out for cases where you want to browse a directory. Maybe... What I mean by this is - according to the docs, if you don't want onMissingTemplate to fire in a folder where you want directory browsing, you have to use the new application variable: welcomeFileList. However, in my tests, this was not the case.
And now for the last note - and this is the weird one. Inside of onMissingTemplate, you do have access to all the scopes you normally would. However - onApplicationStart and onSessionStart are not fired. So you cannot rely on the scopes to exist. You have to test for them. If you use the code like I did above where I simply send the user away, then it isn't a big deal. Once the user is sent to 404.cfm, her session will be created. But it is something to keep in mind.
Archived Comments
I've been secretly hoping they would add that. Right now I've hacked a solution together where we do the same thing, but this will be perfect for the application I've been working on.
Absolutely great!
Ray, I think there is some sort of conflict between the welcomeFileList and directory browsing (or maybe I am missing something). If you say:
"www.dhamra.com/secretfolder" This will not fire onMissingTemplate....
Wouldn't that be the same as viewing a directory for directory browsing? The only difference being that in directory browsing, you call a directory that does exist.
Am I missing something here or is the documentation just not clear?
Ben, what I meant was - if your APp.cfc is in root, and you request secretfolder, it will not fire if secretfolder doesn't exist. If it does exist, you will get directory browsing, or the index.cfm in that folder.
Ray, sorry, I miscommunicated... what I meant to say was that the fact that onmissingtemplate does not fire for missing directories is proof that onmissingtemplate does not have anything to do with directories. From that, I meant to ask, what does it have to do with directory browsing and how would you even define a "directory" for the welcomeFileList? I know it mentions it in the documentation (but I cannot find it - I remember reading it). It confuses me, but it looks like you say not to worry about it anyway.
What I get from the docs is confusing. I'm asking for more clarification and when I get it - I'll post back.
Sweeeeeet :)
I'd be careful with cflocation - it does a "302 temporarily moved" redirect, so you'd have to make sure your 404.cfm had a cfheader with a proper 404 in it...
I think the 302 is bad for search engines as it's effectively saying "the page you requested *does exist*, but is temporarily elsewhere - go there and have a look".
Actually, it turns out the you can now do a status code in cflocation. THe values allowed are 300-307. Would 301 be ok?
"301 HTTP_MOVED_PERMANENTLY - The page
has been assigned a new URI. The change is
permanent."
The welcomeFileList does indeed sound confusing but here's how I believe it should work (this will test my understanding of it!).
Suppose you have index.cfm and default.cfm as your web.xml default welcome pages. If you try to browse to a directory, the system will try to find index.cfm or default.cfm. Normally (with onMissingTemplate() in place), if the file does not exist, it will trigger the handler instead of letting you browse the directory. If you additionally set welcomeFIleList to "index.cfm,default.cfm" then onMissingTemplate() will no longer fire if those files do not exist.
I think :)
I suppose I should just go and test it really...
Sean, I'm not seeing this. Assume app.cfc in /. I request /foo, and foo DOES exist, and has files, but no 'welcome' file. Apache is set to allow directory browsing and thats what I see. The directory browser. My onMissing Template didn't fire since I didn't request a missing thing. If I request /goo, which doesnt' exist, it does work.
So I guess I'm saying - I can't seem to force the issue that makes you even need the welcome pages.
Heh, well, some testing reveals that whatever you specify in this.welcomeFileList will suppress onMissingTemplate() for those named files.
Try setting it to "foo.cfm" and then requesting that file - you'll get the raw CF error.
So it seems something needs to change in the web server setup to make this "work" the way we might expect...
*goes off to do more testing*
So I still don't get it. If /foo exists, and has no welcome file, then the web server either does a browse, OR, it tells you browsing isn't allowed. None of those is a 404. So why would onMissingTemplate even be _considered_ in this case?
Ray - this is cool but the CF8 docs state: 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().
Pity since server-side forward makes it easy to support path styles urls and legacy urls.
Odd. My cflocation code works just fine, and I can think of no reason why not to recommend it. Do note that the docs aren't always correct. ;) I'll try to find out why this was written. The only thing I can figure is that they are worried that you might cflocate to another page that doesn't exist and create a loop.
I figured the same - that the concern is a endless loop. I tested with a server-side forward and it also worked fine. however if it is on the docs then I am hesitant to commit to the solution. Be great if you can find out more. Thanks.