Last week, I wrote about Data Type validation versus Data Value validation. I claimed that ALL of your data type validation should be done by ColdFusion's CFParam tag before you do any other data validation or business logic. When I mentioned this to Javier Julio, he challenged me on this. He asked, "What does that give you? If you're already doing the type validation with the value validation, why bother separating it out?" I mumbled something about separation of concerns, but then hurriedly moved the conversation on. To be honest, I wasn't ready to formally defend my opinions on this matter.
But, a week has gone by and I have had much turkey and beef to fuel the machinery and I feel that now, I am able to briefly discuss why I would move the data type validation to the top of your form processing pages. In a single word, convenience. It is simply more convenient to have data type validation done before your data value validation.
This concept does not become immediatly apparent because we are so used to dealing with FORM data in a single pass. When I started thinking about CFParam, in my gut, I only had a feeling for what I was not able to express until some back and forth comments with Sean Corfield. It was in these comments that I was able to codify what I was feeling: your form processing page is, in its own right, just like a user defined function.
Instead of thinking of your page like this:
- <!--- Param data. --->
- <!--- Process data. --->
... think about it like this:
- <!--- Param data. --->
- <!--- Process data. --->
Because, isn't that really what we are doing? We are submitting data to an action template for processing. The action template doesn't display anything. It just accepts data, processes it, and sets some variable to be used later on in the page flow. Sounds kind of like a function or sub-routine to me.
When you start to think of it this way, you can see that ColdFusion's CFParam tag is really just the template-based equivalent of the CFArgument tag; the CFParam tag is just defining the "arguments" that get submitted to this template for processing. From this point of view, separating out the data TYPE validation from the data VALUE validation, seems almost natural:
- <!--- Define arguments. --->
- <cfparam .... />
- <!--- Process data. --->
Of course, in a user defined function (UDF), you would use the CFArgument tag instead of the CFParam tag, but I am using CFParam above for the purposes of illustrating flow similarities.
In what might seem like a cheap shot, I almost want to answer the challenge with another question. If you don't think that all of your data TYPE validation should be done by CFParam, that it should be mixed with your other validation rules, then I ask you this, do you use Type validation on your CFArgument tags? I would assume that most of you do. Sure, there are those of you who put "any" in order to gain performance increases, but I would guess that the majority of ColdFusion programmers (who use CFArgument), type their function parameters.
And why do we type our function parameters? Convenience! We want to know that by the time our CFArgument tags have executed we can be sure that all of the values in the ARGUMENTS scope are of a data type that we are expecting. When we expect a String, we want to know that a string was passed in. When we expect a Struct, we want to know that a struct was passed in.
I hope that this clarifies my point a bit. Now, I am just saying that this methodology will make your coding more convenient; I am not saying that it is required. I assume that those of you who do NOT type your function parameters and feel like doing your type validation in the meat of your function bodies will see no need to use CFParam for type validation. But, if you do type your function parameters, then shouldn't you also be doing type validation with your CFParam tags? Why mix the responsibilities in one place when the benefits of concern separation have been so apparent in a functionally equivalent environment for so long?
Thanks Javi for challenging me to get my thoughts in order!
Excellent post. I totally agree. The only thing I contend could be different about cfparam is that if the type didn't match, you could use the default instead of throw an error.
Especially on search pages, but with other forms as well, I want to validate the type, but set a default if it fails so the rest of the page can process cleanly. Its a good way to handle security and bad links to a template gracefully. Though I would not do the same for functions.
It's funny that you mention that because a while back I actually said it would be really cool if CFParam would have a "Catch" attribute which would be like a Default attribute that would catch exceptions:
Of course, it might just be easier to have the Default attribute act as the Catch attribute.
I think in most cases, it would make sense to use the default attribute's value if the type validation failed. Maybe the catch attribute should be a boolean that determines if the programmer wants the exception caught. Maybe a better name for this attribute would be something like failOnError. Here's an example:
<cfparam name="startRow" type="range" min="1" max="#qResult.recordCount#" default="1" failOnError="false">
A lot cleaner than wrapping it with cftry/cfcatch.
I like the way you think.
Glad to see I'm not the only one.
I always thought of an onError="default|throw|etc." attribute, but in all reality, what would replace the etc.? Good call brad.
And Ben, if only the "CF Nook and Cranny Analysis" award was mine to give. You add a lot to the community with your posts on these things.
Thanks. I'm seriously considering a cf_param tag (as Ben talked about). If Adobe decides to add this feature in CF9, it would be easy to replace the custom tag. I'm just not sure of the performance hit.... hmmm... who cares... I'm doing it!
Thanks for the word of praise. I love looking into this stuff. Always, trying to find a better way to do things or a new way to look at problems.
I am curious about the processing hit. I don't think it can be TOO much as custom tags are all hooked up at compile time, right? So, I don't think there will be much additional overhead to to calling that as opposed to having inline logic.
What about disabling the type checking in CFCs?
Disable CFC Type Check
When checked, UDF arguments of CFC type is not validated. The arguments are treated as type "ANY". Use this setting in a production environment only.
In my opinion, the type checking that CF enforces is a lot more to do with debugging as it will tell you if you accidentally pass a string where it is expecting a numeric. When you work on a production environment you can turn off the type checking for CFCs and help your application go faster. But not if you are using it as part of your validation.
But that also brings up the point that you would probably do the same type checking in your validation if you didn't do it through the argument.
I guess that a lot depends on you you are coding it.
This is very interesting. I didn't even know that this option existed. I can see how type checking on CFargument tags is more about debugging because it assumes that once you have tested your code, the environment is secure. This might be true most of the time when dealing with CFCs; however, dealing with form posts, the same is not true. The fact that this data is coming from the client makes it, by default, not "secure" in terms of dependability.
Interested about the CFC type checking though... mulling that one over in my mind.
I did a little performance testing on using a ColdFusion custom tag vs. using the CFParam tag.
I think it's a judgment call at this point. Gotta weight the pros / cons of the different approaches.