Simplify Client-side Validation…by Adding a Server
26 September 2013
Traditional web validation techniques typically involve a mix of client-side validation (with a JavaScript library), followed by another round of validation on the server. By necessity, server-side validation needs to be a superset of client-side code to prevent erroneous—or sometimes malicious—data from getting into the system.
Unfortunately, this approach has some flaws. For one, it relies on developers remembering to duplicate validation features on both the client and server , which they often don’t. Even if you use a system that “automatically” applies validation logic in both places, there are cases where a page can’t be validated without checking with the server (validating the uniqueness of a user name comes to mind).
The approach on which we decided for the project I’m currently on leverages the ubiquity of server-side validation with much of the high level of responsiveness of client-side solutions.
Here’s the gist: when we detect that a form is about to be submitted, intercept the request with JavaScript, serialize the form, and send it up to the server. The server performs its validation normally, and either allows the request through, or responds with a serialized object describing exactly why the validation failed.
UPDATE: I’ve built a simple example application (source code) demonstrating this technique.
Keep the client code clean
Say we’re building a sign-up form. We’ll need a “user name” field that we want to make sure unique within the system. Our HTML probably looks something like this:
<input name="userName" type="text" /> |
We could perform this validation with a custom AJAX request (using jQuery, in this example):
$('#userName').on('change', function() { |
…but now we’ve introduced another server endpoint that needs to handle these requests, as well as some JavaScript that’s really only good for verifying user name uniqueness. This code could be optimized to reduce duplication, but it’s still not very extensible. Instead, what if we serialized the entire form and validated it all at once?
$('form').on('submit', function() { |
Now we can check not only the “user name” field’s value, but all of the values of the form at once. The server’s response (which we’ll see how to build in the next section), could look something like this:
{ |
Instead of a simple “yes/no” validation response, we have a rich model representing exactly what’s wrong with the entire form, and with no additional markup (or even client-side code, beyond our single form hijacking call). We can even use the keys in this return object (which correspond to the name
values of form inputs) to highlight the erroneous fields:
var fields = Object.keys(result); |
Add the server component
In this project, built on ASP.NET MVC, we use the Fluent Validation library for validating data sent up from the client, though this approach hardly requires .NET. To get started, we’ll need a model class to store this form’s data:
public class SignUpViewModel |
We’ll also add a custom validator, using the Fluent Validation syntax. This example is relatively sparse, but the Fluent Validation API is quite flexible.
public class SignUpViewModelValidator : |
ASP.NET MVC already has a built-in server-side validation concept, called “Model State”, that Fluent Validation bolts onto. A ModelState
object contains a .NET object–based representation of exactly the server result we want, so all we need to do is respond to invalid requests with it. Another MVC extensibility point, “Action Filters”, gives us this ability:
public class ValidatorActionFilter : IActionFilter |
In short, this filter serializes the ModelState
object and sends it down to the client as JSON with a “400” status code (“Bad Request”). .NET provides a JsonResult
object, but the default serializer misbehaves frequently with all but the simplest of objects to serialize. As a result, we’ve opted to use the Newtonsoft JSON library instead.
You’ll need to attach this filter to your MVC application—if that’s your server-side framework of choice. We’re using it as a global action filter.
Happy validating!
Now that we’ve built a client that’s aware of potential form errors reported in a certain structure, and instructed the server to send down a meaningful model on invalid form submissions, all that’s left is to sit back and enjoy never having to write a client-side form validator again!