404 error handling in ColdFusion on Wheels

Tuesday, March 30th, 2010

This is fairly simple, but I figured that I would share my approach for 404 error pages in ColdFusion on Wheels and see if anyone has a different/better way of doing it. This example demonstrates code used on cfwheels.org.

The strategy

What I really wanted was a function that I could call whenever a given view’s record could not be found.

In my example case, I wanted to handle user IDs in the People Directory that represent records that don’t exist. I had ended up removing user 31 (and a few others), so I wanted to display a helpful 404 error message every time http://cfwheels.org/user/profile/31 was accessed.

404 error page

So the first step was to create the 404 page itself, which I stored at views/main/error404.cfm.

<cfset layout.title = "Page Not Found | ColdFusion on Wheels">
<cfset layout.header1 = "Page Not Found">
<cfset layout.breadcrumbs = ["Page Not Found"]>
 
<!--- 404 error --->
<cfheader statuscode="404" statustext="Not Found">
 
<cfoutput>
 
<p>
    We're sorry. We couldn't find the page that you're looking for. It has either been removed, or perhaps
    you are accessing an inaccurate <abbr title="Uniform Resource Locator">URL</abbr>.
</p>
 
<h2><label for="search-query-404">Search</label></h2>
#startFormTag(controller="search", id="cse-search-box", method="get")#
    <div>
        <input type="hidden" name="cx" value="005724978648843866544:jpej79qhz14" />
        <input type="hidden" name="cof" value="FORID:10" />
        <input type="hidden" name="ie" value="UTF-8" />
        <input id="search-query-404" type="text" name="q" />
        <input type="submit" name="sa" value="Search" />
    </div>
#endFormTag()#
 
<h2>Start from the Home Page</h2>
<p><strong>#linkTo(text="ColdFusion on Wheels Home &raquo;", route="home")#</strong></p>
 
</cfoutput>

The real meat is the fact that I put the <cfheader> 404 reference in the view file. I look at anything that’s sent to the browser as a job for the view to handle, so that’s why I put the call there instead of in the controller file. In fact, because the page is fairly “dumb,” I didn’t put anything in the Main controller.

Rendering helper

I also put a quick render404() function in the base controller at controllers/Controller.cfc so that I wouldn’t need to manually call renderPage(controller="main", action="error404") every time that I wanted to reference this new view. Your preference may be to not do this, but I’ll leave that up to you. :)

<cffunction name="render404" hint="Renders a 404 error page.">
 
    <cfset renderPage(controller="main", action="error404")>
 
</cffunction>

Handling 404 errors in the controller

The last step involved actually using this functionality in the case that an invalid record ID was passed in the URL. So the user/profile action now looks like this:

<cffunction name="profile" hint="Displays user profile.">
 
    <cfset user = model("customer").findByKey(params.key)>
    <cfset loggedInUser = getLoggedInCustomer()>
    <!--- If profile found, show it --->
    <cfif IsObject(user)>
        <cfset sites = user.sites(where="isApproved=1")>
        <cfset plugins = user.plugins(where="isApproved=1")>
    <!--- 404 error if not found --->
    <cfelse>
        <cfset render404()>
    </cfif>
 
</cffunction>

Fairly simple stuff. When loading the user object, I check to see if an object was returned. If not, then show the 404 page. Pretty reusable, and it only requires an additional if/else block in the controller to decide what to do.

Plus the file at events/onmissingtemplate.cfm can just use <cfhttp> to phone http://cfwheels.org/main/error404 in order to display the exact same error message during a more generic “template not found” scenario.

Besides identifying other places in the application to call render404(), that’s pretty much it.

Technorati Tags: , , , , , , , , ,

4 Responses to “404 error handling in ColdFusion on Wheels”

  1. Grant Copley Says:

    This seems like a good way to handle 404 errors. One problem I’ve experienced is Google still indexing my pages that no longer exist. I send those requests to a custom 404 error page that I’ve created, however I have not been using the CFHEADER tag to send a 404 error in the header. I don’t know this for fact but I’m thinking this is what’s causing Google to continue indexing these pages. I’m not sending them a “true” 404 error page.

    Anyways, good post!

  2. Chris Peters Says:

    Good to hear from you, Grant. (Haven’t heard from you in a while!)

    That part involving <cfheader> is very important. Your “not found” page is just a regular web page to Googlebot if you don’t send the 404 header.

  3. George Murphy Says:

    Hi Chris, is there a way to create an onMissingAction method inside the controller that get’s called anytime there is a 404 call on that controller without applying conditional logic?

  4. Chris Peters Says:

    George, the only thing that Wheels can do automatically for you is to run the template in events/onmissingtemplate.cfm. That runs when the client tries to access a controller or action that doesn’t exist.

    The scenario I describe in this post addresses the situation where a record in the DB wasn’t found (or some other similar logic). Right now, there isn’t really a way for Wheels to “know” that a record wasn’t found.

    In a pre-1.0 release of Wheels, it used to throw a Wheels.RecordNotFound exception when a record wasn’t found. It was quite a different way to do things because instead of checking IsObject(user), you would put a try/catch block around the finder call. Writing a plugin to have the finders throw an exception when a record is not found and then have Wheels display a global template would be the best way to have an “automatic” way of handling what you request.

Leave a Reply