Accessibility
Ray Camden

Raymond Camden

coldfusionjedi.com

Created:
19 December 2005
User Level:
Beginner
Products:
Coldfusion

ColdFusion Coding Contest – Part 1: Beginner Lessons Learned

There are many ways one can learn a programming language. Books, blogs, online resources, and even—lord forbid—the documentation. All of these resources can teach you the basics of syntax and application structure. Some may even cover performance best practices as well as security. What most don't do is simply show you how to solve a problem. So, while a book can tell you how to send mail in ColdFusion, it (typically) won't have a more full-featured example, like building a mail system with parental controls and white lists.

What can be even more beneficial is to demonstrate multiple solutions to a problem, and even discussing flawed solutions. Those of us who write code to help others try our best to be perfect. But a lot can be gained by looking at code with bugs, as long as those bugs are pointed out!

Back in September 2005, I had an idea for my blog. I decided to run a contest for ColdFusion beginners. The idea was to have all the contestants solve a simple problem: number guessing. They would create a ColdFusion application that asks a user to pick a number between 1 and 100, and then tries to guess the user's number. The user would tell the application whether the guess was high or low. By deduction, the application would eventually guess the user's number. Sounds simple, right? One of the things I learned from running the contest is that even the simplest set of rules can be misinterpreted.

The goal of the contest wasn't to give away prizes (which is fun, though) but to get a set of beginners to feel brave enough to submit their solution. As I expected, they made some wonderful mistakes—wonderful in that they were exactly the kind of mistakes I expected. These were not mistakes of incompetence but simply inexperience. (To be honest, some of the problems I saw exist in production code as well.) This article focuses on some of the mistakes I found in the entries, as well as some of the nice surprises. At the end of this article you will find a list of all the blog entries for the contest.

Validating Parameters

One of the first things a web programmer learns is that the web is stateless. To get around this fact, web programmers use a variety of methods including form, URL, and cookie variables. A user can manipulate all of these variables, however. So, for example, you wouldn't pass the price of a product through a URL variable and allow the user to change the price of a product. One problem I saw in many of the submissions was a lack of validation when it comes to these parameters.

The second contest entry I received is a good example. See it here: http://ray.camdenfamily.com/demos/contest1/entry2/index.cfm. As you play the game, notice that the state of the game is stored in URL variables. Here is an example of a variable passed as a URL parameter from midway through the game:

http://ray.camdenfamily.com/demos/contest1/entry2/index.cfm?numcurrent=76&numhigh=100&numlow=1&guess=high&counter=0  

Notice the variable value, numcurrent. If you change the variable's value to apple, or any other nonvalid value, the application throws an error.

Note: Because this demo is on my site, you see a nicely formatted error page instead of the generic ColdFusion error. My error handling also sends me an e-mail, so please have fun.

In general, there are a few things you should always do before you use any variable passed from a browser:

  1. Check to make sure the variable exists with the isDefined function—for example, isDefined("url.id"); or the structKeyExists function—for example, structKeyExists(url, "id").
  2. If the value is numeric, check to make sure the variable value is a valid number with the isNumeric function—for example, isNumeric(url.id).
  3. If the value passed is a primary key in a database, ensure that it is greater than zero.

This list is incomplete; it's just a start. Other validation checks to add are as follows:

File Setup

While it is easy to learn syntax, it is a bit more difficult to learn how to organize your code. The fifth contest entry shows a good example of how not to do it. The application has two files that basically repeat each other. The first file posts to the second file, and the second file posts to itself. Obviously this is a problem if you need to update the layout.

You should never have the same code (whether CFML or HTML) in two or more files. As soon as you do so, you are asking for trouble. You may forget to update both files if you change one. Or you might remember but then update the second file incorrectly. In general, never ever repeat anything. In the example shown in the fifth contest entry, the programmer's first file should simply post to itself. This is what is called a self-posting form; it's how I build all of my forms. The following pseudo-code gives you a rough idea of how to set up a self-posting typical form:

If form submitted
    check form
    if ok, send email, or whatever, tell form not to show
    if bad, record errors
End If

If Errors
    Show the errors
End If

If Showing Form
    Show the form
Else
    Show a thank you or other message
End If

Basically the beginning of the file checks for a form submission and either mails the results (or stores them, whichever is appropriate) or makes a list of errors. Next, the file either shows the form (with errors) or acknowledges the submission along with a thank-you to the user. A slightly less–pseudo-code example follows:

<cfset showForm = true>

<cfif isDefined("form.submit")>
	
	<cfset error = "">
    
    <cfif not len(trim(form.name))>
        <cfset error = error & "Include your name, bozo!"<br>
    </cfif>
    
    <cfif not len(error)>
        <!--- Mail the form info --->
        <cfset showForm = false>
    </cfif>
    
</cfif>

<cfif showForm>

    <cfif isDefined("error")>
        <cfoutput>
        <p>
        <b>Please correct the following error(s):</b><br>
        #error#
        </p>
        </cfoutput>
    </cfif>
    
    <form action="test.cfm" method="post">
    Name: <input type="text" name="name" value="">
    <input type="submit" name="submit" value="Send Your Name">
    </form>
    
<cfelse>
    Thanks!
</cfif>

Obviously most forms have a few more fields and many more validation rules. Ideally, it's preferable for the form to retain the user's values. That way, the users don't have to re-enter everything just because they made a mistake.

Not So Obvious Mistakes

Along with some common mistakes, I saw a few things that were a bit more subtle. For example, consider the fourth contest entry. While going through the code, I noticed something odd about these lines:

<cfif NOT isNumeric("session.HiddenNumber")>
    <cfset session.HiddenNumber = RandRange(1,100)>
</cfif>

I couldn't understand why this programmer's code wasn't throwing an error because I never saw session.HiddenNumber defined anywhere. Then I noticed the quotes in the isNumeric() function. He wasn't checking the variable; he was checking the string. Therefore, the result was always false. I suggested the following change, which both corrects the isNumeric issue and checks to see if the variable exists:

<cfif NOT structKeyExists(session,"hiddenNumber") or not isNumeric(session.hiddenNumber)> 
    <cfset session.HiddenNumber = RandRange(1,100)>
</cfif>

The next issue is not a mistake per se, but more of a style preference. Take this as my opinion only. The third contest entry had one line of code (used multiple times) that did two things I disagreed with:

<cfset temp="#StructInsert(Request,'s_CurrentTemplateFile',GetFileFromPath(GetCurrentTemplatePath()),True)#" /> 

First, the structInsert() function, like a few other functions in ColdFusion, returns a value you don't really need. So you could rewrite the line as follows:

<cfset StructInsert(Request,'s_CurrentTemplateFile',GetFileFromPath(GetCurrentTemplatePath()),True) />

Notice that I also removed the quotation mark and pound signs.

Lastly, and this is definitely a personal opinion, the structInsert() function is simply worthless. I'd simply rewrite the line as a straight cfset tag:

<cfset Request.s_CurrentTemplateFile = GetFileFromPath(GetCurrentTemplatePath()) />

Notice how much simpler this is to read.

One of the more interesting parts of this contest was seeing the many different ways people do the same task. Obviously everyone has his or her own way of doing things, but I definitely recommend picking one way for your organization and ensuring that all of your developers stick to it.

Contest URLs

You can find the full list of contest entries below:

To learn more, read ColdFusion Coding Contest – Part 2: Intermediate Lessons Learned.

About the author

Raymond Camden is a software consultant focusing on ColdFusion and RIA development. A long time ColdFusion user, Raymond has worked on numerous ColdFusion books including the ColdFusion Web Application Construction Kit and has contributed to the Fusion Authority Quarterly Update and the ColdFusion Developers Journal. He also presents at conferences and contributes to online webzines. He founded many community web sites including CFLib.org, ColdFusionPortal.org, ColdFusionCookbook.org and is the author of open source applications, including the popular BlogCFC (www.blogcfc.com) blogging application.Raymond can be reached at his blog (www.coldfusionjedi.com) or via email at ray@camdenfamily.com. He is the happily married proud father of three kids and is somewhat of a Star Wars nut.