Validation and Exception Handling: From the UI to the Backend

Sooner or later in your programming career you will be faced with the dilemma of validation and exception handling. This was the case with me and my team also. A couple or so years ago we reached a point when we had to take architectural actions to accommodate all the exceptional cases our quite large software project needed to handle. Below is a list of practices we came to value and apply when it comes to validation and exception handling.


Validation vs. Exception Handling

When we started discussing our problem, one thing surfaced very quickly. What is validation and what is exception handling? For example in a user registration form, we have some rules for the password (it must contain both numbers and letters). If the user enters only letters, is that a validation issue or an exception. Should the UI validate that, or just pass it to the backend and catch any exceptions that my be thrown?

We reached a common conclusion that validation refers to rules defined by the system and verified against user provided data. A validation should not care about how the business logic works, or how the system for that matter works. For example, our operating system may expect, without any protests, a password composed of plain letters. However we want to enforce a combination of letters and numbers. This is a case for validation, a rule we want to impose.

On the other hand, exceptions are cases when our system may function in an unpredicted way, wrongly, or not at all if some specific data is provided in a wrong format. For example, in the above example, if the username already exists on the system, it is a case of an exception. Our business logic should be able to throw the appropriate exception and the UI catch and handle it so that the user will see a nice message.


Validating in the User Interface

Now that we made clear what our goals are, let's see some examples based on the same user registration form idea.

Validating in JavaScript

To most of today's browsers, JavaScript is second nature. There is almost no webpage without some degree of JavaScript in it. One good practice is to validate some basic things in JavaScript.

Let's say we have a simple user registration form in index.php, as described below.

This will output something similar to the image below:

RegistrationForm

Every such form should validate that the text entered in the two password fields are equal. Obviously this is to ensure the user does not make a mistake when typing in his or her password. With JavaScript, doing the validation is quite simple.

First we need to update a little bit of our HTML code.

We added names to the password input fields so we can identify them. Then we specified that on submit the form should return the result of a function called validatePasswords(). This function is the JavaScript we'll write. Simple scripts like this can be kept in the HTML file, other, more sophisticated ones should go in their own JavaScript files.

The only thing we do here is to compare the values of the two input fields named "password" and "confirm". We can reference the form by the parameter we send in when calling the function. We used "this" in the form's onsubmit attribute, so the form itself is sent to the function.

When the values are the same, true will be returned and the form will be submitted, otherwise an alert message will be shown telling the user the passwords do not match.

PasswordDoNotMatchAlert

HTML5 Validations

While we can use JavaScript to validate most of our inputs, there are cases when we want to go on an easier path. Some degree of input validation is available in HTML5, and most browsers are happy to apply them. Using HTML5 validation is simpler in some cases, though it offers less flexibility.

To demonstrate several validation cases, we extended our form a little bit. We added an email address and a website also. HTML validations were set on three fields.

  • The text input username is just simply required. It will validate with any string longer than zero characters.
  • The email address field is of type "email" and when we specify the "required" attribute, browsers will apply a validation to the field.
  • Finally, the website field is of type "url". We also specified a "pattern" attribute where you can write your regular expressions that validate the required fields.

To make the user aware of the state of the fields, we also used a little bit of CSS to color the borders of the inputs in red or green, depending on the state of the required validation.

HTMLValidations

The problem with HTML validations is that different browsers behave differently when you try to submit the form. Some browsers will just apply the CSS to inform the users, others will prevent the submission of the form altogether. I recommend you to test your HTML validations thoroughly in different browsers and if needed also provide a JavaScript fallback for those browsers that are not smart enough.


Validating in Models

By now many people know about Robert C. Martin's clean architecture proposal, in which the MVC framework is only for presentation and not for business logic.

HighLevelDesign

Essentially, your business logic should reside in a separate, well isolated place, organized to reflect the architecture of your application, while the framework's views and controllers should control the delivery of the content to the user and models could be dropped altogether or, if needed, used only to perform delivery related operations. One such operation is validation. Most frameworks have great validation features. It would be a shame to not put your models at work and do a little validation there.

We will not install several MVC web frameworks to demonstrate how to validate our previous forms, but here are two approximate solutions in Laravel and CakePHP.

Validation in a Laravel Model

Laravel is designed so that you have more access to validation in the Controller where you also have direct access to the input from the user. The built-in validator kind of prefers to be used there. However there are suggestions on the Internet that validating in models is still a good thing to do in Laravel. A complete example and solution by Jeffrey Way can be found on his Github repository.

If you prefer to write your own solution, you could do something similar to the model below.

You can use this from your controller by simply creating the UserACL object and call validate on it. You will probably have the "register" method also on this model, and the register will just delegate the already validated data to your business logic.

Validation in a CakePHP Model

CakePHP promotes validation in models as well. It has extensive validation functionality at model level. Here is about how a validation for our form would look like in CakePHP.

We only exemplified the rules partially. It is enough to highlight the power of validation in the model. CakePHP is particularly good at this. It has a great number of built-in validation functions like "minLength" in the example and various ways to provide feedback to the user. Even more, concepts like "required" or "allowEmpty" are not actually validation rules. Cake will look at these when generating your view and put HTML validations also on fields marked with these parameters. However rules are great and can easily be extended by just simply creating methods on the model class as we did to compare the two password fields. Finally, you can always specify the message you want to send to the views in case of validation failure. More on CakePHP validation in the cookbook.

Validation in general at the model level has its advantages. Each framework provides easy access to the input fields and creates the mechanism to notify the user in case of validation failure. No need for try-catch statements or any other sophisticated steps. Validation on the server side also assures that the data gets validated, no matter what. The user can not trick our software any more as with HTML or JavaScript. Of course, each server side validation comes with the cost of a network round-trip and computing power on the provider's side instead of the client's side.


Throwing Exceptions from the Business Logic

The final step in checking data before committing it to the system is at the level of our business logic. Information that reaches this part of the system should be sanitized enough to be usable. The business logic should only check for cases that are critical for it. For example, adding a user that already exists is a case when we throw an exception. Checking the length of the user to be at least five characters should not happen at this level. We can safely assume that such limitations were enforced at higher levels.

On the other hand, comparing the two passwords is a matter for discussion. For example, if we just encrypt and save the password near the user in a database, we could drop the check and assume previous layers made sure the passwords are equal. However, if we create a real user on the operating system using an API or a CLI tool that actually requires a username, password, and password confirmation, we may want to take the second entry also and send it to a CLI tool. Let it re-validate if the passwords match and be ready to throw an exception if they do not. This way we modeled our business logic to match how the real operating system behaves.

Throwing Exceptions from PHP

Throwing exceptions from PHP is very easy. Let's create our user access control class, and demonstrate how to implement a user addition functionality.

I always like to start with something simple that gets me going. Creating a stupid test is a great way to do so. It also forces me to think about what I want to implement. A test named UserControlTest means I thought I will need a UserControl class to implement my method.

The next test to write is a degenerative case. We will not test for a specific user length, but we want to make sure we do not want to add an empty user. It is sometimes easy to lose the content of a variable from view to business, over all those layers of our application. This code will obviously fail, because we do not have a class yet.

Let's create the class and run our tests. Now we have another problem.

But we can fix that, too, in just a couple of seconds.

Now we can have a nice test failure telling us the whole story of our code.

Finally we can do some actual coding.

That makes the expectation for the exception pass, but without specifying a message the test will still fail.

Time to write the Exception's message

Now, that makes our test pass. As you can observe, PHPUnit verifies that the expected exception message is contained in the actually thrown exception. This is useful because it allows us to dynamically construct messages and only check for the stable part. A common example is when you throw an error with a base text and at the end you specify the reason for that exception. Reasons are usually provided by third party libraries or application.

Throwing errors on duplicate users will allow us to explore this message construction a step further. The test above creates a mock which will simulate a system command, it will fail and on request, it will return a nice failure message. We will inject this command to the UserControl class for internal use.

Injecting the a SystemCommand instance was quite easy. We also created a SystemCommand class inside our test just to avoid syntax problems. We won't implement it. Its scope exceeds this tutorial's topic. However, we have another test failure message.

Yep. We are not throwing any exceptions. The logic to call the system command and try to add the user is missing.

Now, those modifications to the add() method can do the trick. We try to execute our command on the system, no matter what, and if the system says it can not add the user for whatever reason we throw an exception. This exception's message will be part hard-coded, with the user's name attached and then the reason from the system command concatenated at the end. As you can see, this code makes our test pass.

Custom Exceptions

Throwing exceptions with different messages is enough in most cases. However, when you have a more complex system you also need to catch these exceptions and take different actions based on them. Analyzing an exception's message and taking action solely on that can lead to some annoying problems. First, strings are part of the UI, presentation, and they have a volatile nature. Basing logic on ever changing strings will lead to dependency management nightmare. Second, calling a getMessage() method on the caught exception each time is also a strange way to decide what to do next.

With all these in mind, creating our own exceptions is the next logical step to take.

We modified our test to expect our own custom exception, ExceptionCannotAddUser. The rest of the test is unchanged.

The class that implements our custom exception is like any other class, but it has to extend Exception. Using custom exceptions also provides us a great place to do all the presentation related string manipulation. Moving the concatenation here, we also eliminated presentation from the business logic and respected the single responsibility principle.

Throwing our own exception is just a matter of changing the old "throw" command to the new one and sending in two parameters instead of composing the message here. Of course all tests are passing.

Catching Exceptions in Your MVC

Exceptions must be caught at some point, unless you want your user to see them as they are. If you are using an MVC framework you will probably want to catch exceptions in the controller or model. After the exception is caught, it is transformed in a message to the user and rendered inside your view. A common way to achieve this is to create a "tryAction($action)" method in your application's base controller or model and always call it with the current action. In that method you can do the catching logic and nice message generation to suit your framework.

If you do not use a web framework, or a web interface for that matter, your presentation layer should take care of catching and transforming these exceptions.

If you develop a library, catching your exceptions will be the responsibility of your clients.


Final Thoughts

That's it. We traversed all the layers of our application. We validated in JavaScript, HTML and in our models. We've thrown and caught exceptions from our business logic and even created our own custom exceptions. This approach to validation and exception handling can be applied from small to big projects without any severe problems. However if your validation logic is getting very complex, and different parts of your project uses overlapping parts of logic, you may consider extracting all validations that can be done at a specific level to a validation service or validation provider. These levels may include, but not need to be limited to JavaScript validator, backend PHP validator, third party communication validator and so on.

Thank you for reading. Have a nice day.

Tags:

Comments

Related Articles