logo

Server-Side Error 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...

Feedback

    • avatar
    • Jorge
    • Mon 28 Jun 2004

    Server-Side is Great for Wireless Development

    I've been working on developing a number of web applications for wireless devicies (i.e. Pocket PC, Blackberry, etc.) and the serve-side validation is the way to go here. With most devices having limited or no JavaScript support, it's simply easier to let Domino take care of the validation. Great article Jake.

    • avatar
    • veer
    • Mon 28 Jun 2004

    user entered values

    Great job Jake!! I have a question which I could've answered myself if I had downloaded your demo database ( I will surely do it today ). But looking purely at your code, when you do server side validations and return the form back to the user, the values user entered are no longer there.

    Is there a trick to retain the values so we don't make the user go through the pain of re-typing each and every field?

      • avatar
      • Jake
      • Mon 28 Jun 2004

      Re: user entered values

      Not sure what you mean veer. Field values *are* retained. Have you seen them not get saved? What were you doing when this happened? Or are you assuming this from reading the code? Never assume anything with Domino! ;o)

      Jake

      Show the rest of this thread

  1. @Success and @Failure

    Hi Jake,

    As usual your article is excellent. I ignored the way one writes a script that disables validation. Your demonstration is really convincing.

    In your article you do not mention the functions @Failure and @Success. One can use them the following way in a field's validation (in this example the field is called MyDate):

    Ai := "<BODY><SCRIPT LANGUAGE=\"JavaScript\">" ; Msg := "alert(\'The field DATE is mandatory\')"; Af := ";history.go (-1) ;</SCRIPT></BODY>" ; MsgError := Ai + Msg + Af ; @If(MyDate="";@Failure(MsgError);@Success)

    It's a server side validation too. Don't you like this way of managing it?

    PS: I take this opportunity to congratulate you about the excellent entries you recently wrote about DHTML: date picker, optgroup and color picker. That's really great.

    Kind regards, Lionel http://double-six.org/

      • avatar
      • Jake Howlett
      • Tue 29 Jun 2004

      Re: @Success and @Failure

      Hi Lionel,

      Glad you like the article ;o)

      I've seen the use of @Failure and JavaScript .back() combined before but I don't like it. It's an annoying way for the user to have to validate their entry.

      The real issue with it is that not all browsers retain field values when you go back to a page in the history. Mozilla is better at it than IE, which tends to always blank out fields.

  2. Great Article!

    Excellent Article Jake!

    I do have an application that captures customer information or sale leads. Its a web form that a customer would fill in. There are manditory fields on it and I used Javascript for validation. I thought it was a good idea to use Javascript as the previous system was that the $$return field would take you to a page that would tell you that some of the manditory fields were missing. It would not tell you which one etc. You would then have to push back on the browser (shudders).. But this your article shows a great alternative...

    Allen

    • avatar
    • Al
    • Tue 29 Jun 2004

    Security Issues?

    Since our skill group where I work is still developing in this area, I have a question concerning security.

    If one can define and re-define the JavaScript functions within the browser address bar this leads me to ask, is this not a big security hole? Im not an expert on JavaScript and domino but can you trigger agents via JavaScript? What sort of "damageĀ£ can you do using this method. This concerns me but perhaps its not an issue.. Any ideas?

    Allen

    1. Re: Security Issues?

      Trigger an agent with JavaScript? How? If they know the name of the agent then they can simply type its name followed by ?OpenAgent and press enter.

      Show the rest of this thread

  3. Server Side is mandatory

    Jake,

    I'm really glad you wrote about this. I've seen your past articles on validation, which have typically been javascript, and always thought that they are nice thing but miss the point.

    There are so many ways to hack a web page and avoid javascript validation that its almost not worth doing in my mind. I've seen lots of developers say how much better it is because of saving the roundtrip and processing on the server, and those are the developers I think that must spend their day weedling muck out of their databases due to hack attempts etc. or they are very lucky.

    For me, server side validation is a must. And not just relying on database validation or whatever, but proper validation with user friendly markup returned to the user to help them complete the process. Adding javascript later as a double layer and to prevent the need and delay of a round trip is a bonus, but doesn't get away from the need for server side in the first place.

    I've taken this approach with my recent PHP efforts. I've built a validation class that can be applied to the input fields, and the validation is run on the server side in every case. As needed, I'm planning on supplementing the validation class with client side code insertion so that certain validations can be pushed out to the client, but with the server still validating on the post.

    Cheers Dave

    1. Re: Server Side is mandatory

      Hey Dave,

      I quite agree - server-side is the end-all of form validation. However, I do think javascript validation has its place: as a courtesy to the user.

      This adds workload and maintenance, but if you're keeping usability (user-friendliness) in mind, while not strictly necesary, client-side on top of server-side validation is a courtesy folks will appreciate. And, as Jake has demonstrated, while it can be bypassed, there's little harm that will come of it.

      Great article Jake. (Send it to me for proofing next time in advance of publishing ;-) )

      Jerry

    • avatar
    • LL
    • Wed 30 Jun 2004

    Another way to do it...

    More and more i use this way to validate forms.

    I create a field called JSRunner wich I set as Computed for display. Before the field i write <script language=JavaScript> and after the field </script>. And sets it's to pass thru html. Should look something like this.

    <script language="JavaScript> [JSRunner] </script>

    Then in my submit button i have a simple code like this.

    errorMsg := @If( myField = "" ; "No value" ; "" ); @if( errorMsg = "" ; @Success ; @Return( @SetField( "JSRunner" ; "alert('" + errorMsg "')" ))) ; @Command([FileSave]) ; @Command([FileCloseWindow]) ;

    / LL

    • avatar
    • AdFos
    • Thu 8 Jul 2004

    Even easier server side validation

    See: http://www.notesninjas.com/#InputValidation

  4. validate and run agents

    Lo all, Was interested in moving some of our forms away from only-javascript validation for all the reasons described in the article and posts above. My only worries were 1. I run lots of agents in the WebQuerySave event and 2. I usually lock users out of the form once posted.

    So my final solution needed to address both of these as well as the standard issues of holding already posted data etc...

    Found that the above post does make server-side validation quite manageable.

    My only issue is the amount of hand coding that goes into the WebQuerySave. You have to identify all the fields and how you want to validate them.

    That said here is my contibution. Like I said, I tend to run lots of agents post submit so here is my WebQuerySave.. shows validation followed by agents running. Formula language still so simple to work with.

    @If ( DeleteDocument = "Delete" | ArchiveDocument = "Archive" ; "" ; @Do (

    errors:=""; @If( Title=""; errors:=errors:"Title is required"; "" ); @If( CapabilityFocus1=""; errors:=errors:"Capability is required"; "" ); @If(@Elements(errors)>0; @Do(@SetField("hasErrors"; 1); @SetField("Errors"; errors); @SetField("SaveOptions"; "0")); @Do(@SetField("hasErrors"; 0); @SetField("Errors"; ""); @SetField("SaveOptions"; "1")) );

    @Command([ToolsRunMacro]; "(agent1)") ; @Command([ToolsRunMacro]; "(agent2)") ; @Command([ToolsRunMacro]; "(agent3)")))

    1. Re: validate and run agents

      Got a little longer chance to play with this.

      Wanted to make the webQuerySave even a little more generic and move the stuff that changes to a field on the form.

      so... 1. added a field called "IsRequired" on the form (you could have a few of these). 2. Added a while loop to test against contents of this field.

      Code follows:

      errors:=""; n := 1; @While( n <= @Elements(IsRequired); @If( IsRequired[n]="" ; errors:=errors:("One of the Required fields is Empty"); "" ); @If( @Length(IsRequired[n])>1000 ; errors:=errors:"One of the Required fields is too long"; "" ); n := n + 1);

      @If(@Elements(errors)>0; @Do(@SetField("hasErrors"; 1); @SetField("Errors"; errors); @SetField("SaveOptions"; "0")); @Do(@SetField("hasErrors"; 0); @SetField("Errors"; ""); @SetField("SaveOptions"; "1")) );

  5. Running server and client side

    Good bits of code Jake - like the server and client side validation. What I don't know how to do is to use them both with the JS validation being the first level and then if someone hacks it to turn them back at the server as well. When I try to use both methods pieces of code I get an error. Anyway around this?

    • avatar
    • tim
    • Wed 25 Aug 2004

    Good stuff, but better formula/fields?

    Nice article, and as usual, I wish you had kicked this out a couple of months earlier, when I struggled on with my own version after finding nothing sensible 'out-there' for server validation!

    Anyway, just a couple of thoughts...

    I build my formula error-checks in a computed multi-value field, say: errorChk like:

    @trim( @(Name=""; "No Name"; "") : @if(Tel=""; "No Tel"; "") )

    The end result is a either a null (no errors) or a list of error strings. Then simply check the field for null or otherwise:

    @If(ErrorChk = ""; @doThis; @doThat)

    Its easier and more elegant than setting/clearing the field and the boolean test is easy too! (whats all this @elements>0 stuff eh?).

    When calling a WQS Agent, the computed error-message list is easy to access:

    if thisdoc.ErrorChk(0) = "" then...

    Its even possible to send an LS error-list back to the reopened document so that the user is shown a list of on-doc and wqs-agent generated messages 'in context'

    Anyway, missed the 'save only' trick on the submit!

    Now, couple of queries:

    1. I have experienced issues where a save only triggers a WQS event - so the agent runs, if its not a save/close event!! Any work arounds?

    2. When writing 'public' docs with user=reader acl access, you can create docs, edit them, but not save them (!) if saveoptions=1 - which is a pain! So how recover if saveoptions=0 and want to get the data back in the form? when its reopened? (okay - I think - if use author with create rights!)

    any help appreciated - chrz +T+

      • avatar
      • Jake
      • Wed 25 Aug 2004

      Re: Good stuff, but better formula/fields?

      Not sure about point 2, but with point 1 you may be able to solve it thus:

      You're right, in that WQS agents run even when you're not saving the actual document. Howabout testing if it is being saved in the WQS event formula. Like so:

      @If( @IsDocBeingSaved; @Command([ToolsRunMacro]; "(myWQS)"); "")

      If this don't work then maybe set a flag when the save button is pressed and check for this in the WQS event.

      Show the rest of this thread

    • avatar
    • tim
    • Fri 27 Aug 2004

    Any ideas on how to server-validate a non-save doc

    Hmmm... anybody any thoughts on how to server-side validate a new doc you don't want to save until it is 'fixed'?

    If saveoptions=0 and you want to preserve the form content so far, how do you server-side validate it, keep the data and only save it when it is clean?

    My best guess so far is to run a wqs, then validate, clear the saveoptions back to 0 if fails, and reopen a new form and pass all the data back into the new doc by either URL parameter (boo!) or wqo?

    It looks possible, but what-a-faf! (technical term)!

      • avatar
      • Jake
      • Fri 27 Aug 2004

      Re: Any ideas on how to server-validate a non-save

      If I understand you right, you've just described exactly what this article describes.

      Jake

      Show the rest of this thread

  6. Server side validation for disabled users

    Please note that this method is actually a great benefit to partially sighted users who often turn off JavaScript. Indeed, a website will not pass a compliancy ruling by the RNIB (Royal National Institute of the blind) if validation of forms using javascript is used. That goes for a lot of other things to do with accessIbility. Just one thing I struggled with but found out in the end. You need to turn on 'Use JavaScript when generating pages'. Otherwise, it does not work. I expect someone will put me right on that?

    1. Absolutely wrong!

      This is absolutely wonderful except for the little tiny thing... It doesn't work!

      The goal is to perform the validation even if the browser has turned of JavaScript (or the browser just can't handle JavaScript). The solution depend on the database has checked "Use JavaScript when generating pages". If you turn of that, you won't be able to create a new page. If the browser turns of JavaScript it will not work either.

      However I was set on the right track bye reading this article. The solution is to use WebQuerySave, to check some computed fields, so that you can set some fields before opening the same form again. When all fields are ok after validation, you use $$Return-field to open the document instead of editing the document.

Your Comments

Name:
E-mail:
(optional)
Website:
(optional)
Comment:



Navigate other articles in the category "Forms"

« Previous Article Next Article »
Making Domino Behave Relationally   Domino Rich Text In The Browser

About This Article

Author: Jake Howlett
Category: Forms
Keywords: error; server-side; validation;

Attachments

validation-v1.0.1.zip (87 Kbytes)

Options

View Online Demo
Feedback
Print Friendly

Let's Get Social


About This Website

CodeStore is all about web development. Concentrating on Lotus Domino, ASP.NET, Flex, SharePoint and all things internet.

Your host is Jake Howlett who runs his own web development company called Rockall Design and is always on the lookout for new and interesting work to do.

You can find me on Twitter and on Linked In.

Read more about this site »