Firstly, these “defensive code” articles are not meant to be a definitive, just practices I feel are important to the way I code. The reason I’m compiling this list is partly so that I can have a reference for myself, but also because I wanted to present something to the team I work in.
There are two types of input validation:
- Input from an external source; and
- Input from within the application.
An external input may be a user submitting a form or it may be the stream from a file you are importing. Input from within your application may simply be the passing of variables as parameters from on method to the next.
In both cases I choose to take the stance that input is almost always likely to be invalid or just plain wrong.
Regardless of the input, it is imperative that you check:
- The input exists
There is no point further validating a null string or stream. This should be the first step in ANY input validation.
- The data should be of the correct format
If a user inputs a value into a decimal field, it should be validated to ensure that the decimal has the correct point accuracy, e.g. 2 decimal places for a monetary value. This can be done without the need to cast the value.
- The input data is of the same type that is expected
If a user inputs a value into a decimal field, the value should be validated to ensure that it is a decimal and not a string.
- The input data is within the correct boundaries
Obviously, there are scenarios where this is overkill. For example, you wouldn’t need to check that a textual input is of a string type! But regardless of the input method, these can all be applied to ensure that the data collected is valid.
I think controlling internal input is easier, just more time consuming. Essentially, for any method, you should check that the values passed or pulled in are valid. Again, there are scenarios where this is not required but my personal rule is to assume that input is invalid.
You will hardly ever have to validate parameterized input is of a specific type as this can be defined by the parameter type. However, all the guidelines from above can be applied.
- Data exists;
- Data is in the expected format;
- Data is within the correct boundaries.
With values that are pulled in, i.e. not parameterized but maybe from a global variable, it is even more important that the guidelines from above are adhered to. This is because otherwise there is an assumption that some other process is validating the data pulled in, which of course you cannot rely on.
Consider the case that a class has a username property which is used in a method but not passed in via a parameter. In this case, it is worth having a separate, global method for handling the validation of the username, i.e. IsUsernameValid(). This way, other routines can make use of the same validation method.