Server-Side Error Validation

Jake Howlett, 28 June 2004

Category: Forms; Keywords: error server-side validation

As Domino Developers we all use forms on a day-to-day basis. We spend most of our working life dealing with data submitted by our users. It goes without saying then that we should be familiar with validating this data for any errors.

It's long been the accepted standard that this is best done at the browser using JavaScript. What I want to do in this article is offer an alternative, server-side, solution and argue why, sometimes, this might be the better option.

What's wrong with JavaScript?

There's nothing really wrong with using JavaScript to do data validation. It's the quick and easy approach. It also means there's no need for the user to wait for a round-trip to the server before being told of errors. However, it's not fool-proof. Well, ok, it would take more than a fool to get round JavaScript validation but, what I mean is, if somebody really wanted to, they could easily bypass the validation and submit false data.

Whether or not your application needs to worry about people submitting spurious information is another thing. If you've no need to worry then JavaScript is probably ok for you. If you want to really make sure you're getting exactly what you want then you need another approach that's more reliable.

How to get round JavaScript validation?

Consider the form below, which is a working copy of this one. Fill in the fields, press Save and it will create a live document in that database. Note: You can download this database from the bottom of this article.

As it stand the form here use JavaScript to validate data. Press the save button now before you fill any fields in. Pretty standard stuff really. Miss a field and it prompts you to fill it in. Once all fields are filled in you can create the document.

Name (any non-blank string):


Age (number from 0 to 130):


Country Code (Any 2 characters):





Now let's see how we can break this. What we want to do is turn off this validation and let us submit anything we like or maybe nothing at all.

After a little digging in the HTML we can see that the Save button above calls a function called "doSubmitForm". This function does some basic validation before calling the submit() method of the form.

Taking advantage of the fact that everything in the browser is a part of the DOM we can use some scripting to make changes to this. For example we can easily re-write the doSubmitForm function. In the code below I've re-defined the object as a new function. In this function we simply display a message and submit the form. No validation. The code below is a link. Click it and the code will run. Now press the save button. Even with all the fields empty it will submit the form. We've by-passed the validation!

JavaScript:void( doSubmitForm=new Function('alert(\'Validation by-passed!!\');document.forms[1].submit();') )

Now, I know this is a link on the page that wouldn't normally be on any form, but you can run the same code by typing it in to the address bar. Refresh this page (to re-assign the original function), copy the above line in to the address bar and press enter. Now press the save button and you will again have by-passed validation.

There's an easy way that with this form as it has no onsubmit event. We can take advantage of this by simply calling the form's submit() event for ourselves, straight from the address bar, by-passing the validation function all together. Click the link below or paste in to the address bar to see what I mean.

JavaScript:document.forms[1].submit();

So, as you can see, it's fairly easy to get round basic JavaScript validation. Add to this the fact that more and more people are running browsers with JavaScript disabled and you have two good reasons to consider a server-side alternative.

Server-Side Validation With Domino:

The approach to data validation on the server is slightly different. Here, the user submits the form in whatever state they choose. The server then analyses the data and, if there's an error. returns the form back to the user with the errors highlighted. The user then tries again and this process goes on until all is ok.

What I should have called this article was Easy Server-Side Validation. Easy because I am going to use @Formula instead of LotuScript. It can of course be done in LotuScript (or even Java if you feel like it) but I prefer to keep things simple whenever I can.

The approach I used to get this to work is simple. On the form I added a field called SaveOptions with a default value of "1". As we all know, if this value is "0" the document doesn't get saved. All we need to do then is get our error validation to change it to zero if and when errors our found.

There is also a pair of fields called "hasErrors" and "Errors". The first is a pseudo-boolean number field set to either 1 or 0. The second holds the list of errors that are displayed to the user if needs be.

All three fields are set from our validation "script" which runs from the WebQuerySave event. A little-know n fact is that a form's WebQuerySave event can not only contain the name of the agent you want to run but can contain a list of @Function calls to run instead. In this case, something like:

errors:="";
@If( Name=""; errors:=errors:"Name is required"; "" );
@If( Age="" | @IsError(@TextToNumber(Age)) | @TextToNumber(Age)<0 | @TextToNumber(Age) > 130; errors:=errors:"Age must be a number"; "" );
@If( Country="" | @Length(Country)!=2; errors:=errors:"Country must be a two char code"; "" );
@If(@Elements(errors)>0;
@Do(@SetField("hasErrors"; 1); @SetField("Errors"; errors); @SetField("SaveOptions"; "0"));
@Do(@SetField("hasErrors"; 0); @SetField("Errors"; ""); @SetField("SaveOptions"; "1"))
)

So, each time you submit a document, whether it's new or being edited, this script runs. First thing it does is set the errors variable to nothing. Then a series of @If() calls are used to validation each required field. Each time an error is found its description is added to the list. If errors were found the hasErrors flag is set to "true", the "Errors" field is populated with the list and most importantly "SaveOptions" is set to "0". If no errors were found then the reverse is true and the document saves ok.

Now for a clever little trick we need to use to keep the field values in the form after it's submitted, even where there are errors. It's simple to do. All we need is a save button with the following @Function in it. If there are errors all we do is save the document. If there are no errors we also close it and trigger the $$Return logic. If there are errors the simple save will add the familiar &Seq=1 to the URL and open the same page over and over, increasing this "seq" number, until all errors are gone.

@Command([ FileSave ]);
@If(hasErrors; ""; @Command([ FileCloseWindow ]) )

The only other field we need to worry about is the $$Return field. Because this field only has an effect when we combine the FileSave and FileCloseWindow command we can assume there are no errors at this point and simply open the document in read mode.

"[ /" + @WebDbName + "/0/" + @Text(@DocumentUniqueID) + "?OpenDocument") + " ]"
Now all that's left to do is display any errors to the user. This is simple to do. Add a Computed Value text area to your form and use it to format the list from the "Errors" field, like so:

@If(hasErrors; "[ <p>There are errors:<ul>" + @Implode( "<li>" + @Trim( Errors ); "</li>" ) + "</li></ul></p> ]"; "")
The result is a bullet list of errors. It might be an idea to mark this Computed Value as red text to help makes the errors stand out.

And there you have it. That's all that's needed to shift your error validation to the server.

Summary:

Hopefully I've convinced you of one thing - JavaScript is not a safe means by which to validate data submitted on the web. If your application holds mission critical data you really want to make the validation tamper-proof. To do this you need to either scrap JavaScript in favour a server-side solution or use a combination of the two. Needless to say, even the most advanced of junior hackers won't be able to tamper with the code on the server.

The server-side code and method I've described here is about as simple as I could make it. The problem some of you may find is that you can't use @Functions in the WebQuerySave event like I've done, as you already run an agent. You can always have the validation code run and then call the agent, but it may be best to move the logic in the agent itself. This shouldn't be too hard to do at all.

So, there you have it - simple server-side error validation. Download the sample database and have a play with it...