This is more just a consolidation of some previous entry's thoughts, but I thought it was be a good solid topic unto itself. As I have been working on my Exercise List project, I have been doing a tremendous amount of thought about data validation and I've had a bit of a revelation: there is a very distinct difference between data TYPE validation and data VALUE validation.
A long time ago, when I posted about an improvement that I would like to see in ColdFusion's CFParam tag, I hadn't yet understood this divide. As such, I was not able to articulate my thoughts very well or defend my idea against nay-sayers. But now, I feel like things have become much more clear. Data type validation and data value validation are separate concerns and should be handled by different parts of the codebase.
When you look at this, you start to see how powerful and useful ColdFusion's CFParam tags really are. You can use it to run all of your data TYPE validation. My new goal, and so far, I am not seeing a problem with this, is the establish that once my CFParams have finished executing (for a form or URL submission), all data in that form should be of the correct TYPE. This does not mean that it will contain valid data in terms of the business logic, but rather, if I was expecting a numeric value, I have a numeric value or if I was expecting a list, I have a list.
When I think about the data that comes across in a FORM or URL submission, I really only see three kinds:
- Numeric data such as the ID selected in a select box or boolean (1/0) sent over in a checkbox.
- String data such as the text submitted with text input, and textarea.
- List data, which is the result of multiple fields with the same name being submitted.
I can't really see anything else that falls outside of one of these three categories. Even binary data, such as uploaded file information, still corresponds to a string value in the form (not to the resultant binary upload).
So where does CFParam come into play here? CFParam should be doing all of the data TYPE validation before the form is really even processed. Theoretically, data types should never be an issue unless a user has tampered with the form submission process. Meaning, all text input should submit strings; all select boxes (that have ID values) should return numbers. It is only through user tomfoolery that any of these data types should ever fail type validation. As such, the argument against "expensive" exception handling is moot. Data type exceptions will be presented only to the users that invite them through their own mischief.
While no standard user should have a data type exception, it does not mean that we should not handle them gracefully. As such, all CFParams that can throw data type exceptions, should either be handled by the application error handler, or even better yet (IMO), handled by an individual CFTry / CFCatch block:
<cftry> <cfparam name="FORM.user_id" type="numeric" default="0" /> <!--- Catch TYPE validation error. ---> <cfcatch> <cfset FORM.user_id = 0 /> </cfcatch> </cftry>
Here, we are enforcing that an ID (probably from some sort of select box) must be numeric; and, if it is not numeric, then we are forcing it to zero (which in my systems, is usually the same as if no selection was made in the first place). This has nothing to do with whether or not the user ID is valid in the system (ie. that is corresponds to a record in our database), it is only saying that the ID value must be of Type, numeric.
Here's where I think a lot of people get confused (myself included): text inputs that require numeric data. Take for example, the price of an eCommerce product. This is entered in a text input box, but from a business standpoint, it should only be a numeric value. People see this, and might think that they need this type of CFParam:
<cfparam name="FORM.price" type="numeric" default="0" />
The problem here is that the price might not contain a valid number. What if a user leaves it blank? What if a user entered alpha characters? This would cause the form field to fail Type validation and to throw an Exception. This is why, I believe, people think that CFParam is a poor choice for validation and that Exception handling in this case is very expensive, and all that good stuff. The problem is, they are basing this feeling off of a scenario that is not valid.
See, the price field is NOT numeric. It's a string. Text inputs and textareas can ONLY submit string values (although I guess it could be argued that ALL form fields can only submit string values, but let's not go there). There is nothing about a text input interface (outside of client side scripting) that confines the user to a given set of characters. As such, there is no way we should ever think of validating its type as Numeric. Is can only and will only ever be a string value.
Once we get the price field into the server side environment, it is only our business logic that determines what the valid VALUES of that field are. It is only our business logic that states that price must contain a numeric value. It is only the job of the business logic to validate the VALUE of a variable.
Ok, so you might look at this and ask, How can we ever say that a form value is anything but a string? It has to do with the user interface. Select boxes, hidden values, check boxes, and radio boxes don't allow the user to enter a value; they only allow the user to select which value will be submitted with the form. As such, the TYPE of data submitted by these form fields is locked down. Similarly, if a user were to select multiple checkboxes which would submit a comma delimited list of values, the list type is still set in stone as the user should not be messing with the submitted values but rather only selecting which of the values will be submitted.
I hope that some of this is making sense. I am not the most articulate person - I am a code monkey. But, I hope that maybe you are seeing a bit of revelation that I saw; that TYPE and VALUE validation are two very different concerns. And, I hope that by seeing this separation, you are seeing that ColdFusion's CFParam tag is a wonderful tag. But, you might wonder, if the business logic is what determines the valid Values of a variable, why bother doing type checking with CFParam at all? The simple, powerful answer - environmental confidence. You will never have to worry about the type of data that you are dealing with. If you are expect a numeric ID from a select, you will be confident that you are dealing with a numeric value. Additionally, I hope that you will also see that data type exceptions will only be the edge case and will only happen to users who invite it with acts of naughtiness.
Want to use code from this post? Check out the license.