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

      Hide the rest of this thread

        • avatar
        • veer
        • Tue 29 Jun 2004

        Re: user entered values

        Jake, You are right, I did not download your demo database and made the assumption purely based on code. After having downloaded the demo database, I have to say, I am very very impressed. Great job!!

        But, I am trying to figure out, why? The $$return points the new documents to "?OpenForm" and old ones to "?editDocument". How does the server do the "&Seq=1" on it?

          • avatar
          • Jake Howlett
          • Tue 29 Jun 2004

          Re: user entered values

          Woops! I wrote the article and forgot about another trick needed. I was wrong to fool people in to thinking the $$Return field had anything to do with the logic. Sorry. The trick is that the Save button has this formula in it:

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

          So, it only closes the page if there are no errors. $$Return only kicks in when CloseWindow is triggered. Hence until hasErrors=false it will simply save and so you see &Seq=1

            • avatar
            • veer
            • Tue 29 Jun 2004

            Re: user entered values

            Awesome Jake!!!

            I can't imagine how you found all these hacks!! Great job :-)

            Now, let me ask you one final question ( i know i ask too many questions and thats why the people who know me wont let me get started :-) ).

            When I do javascript:document.forms[0].submit(); from the address bar, i get a 404 error message. Its fine b'cos it stops people from putting bad data, but, I use a lot of javascript in my submit buttons ( I have never used @command filesave in web, irrespective of me doing validation or not ). Is there a way to make this work with javascript?

              • avatar
              • Jake
              • Tue 29 Jun 2004

              Re: user entered values

              Not sure what the question is veer. Which page are you getting a 404 from with that script? What do you want to get to work exactly?

              Jake

                • avatar
                • veer
                • Wed 30 Jun 2004

                Re: user entered values

                I am getting 404 from this page

                http://www.codestore.net/apps/validation.nsf/contact?OpenForm

                when i do javascript:document.forms[0].submit();

                  • avatar
                  • Jake
                  • Wed 30 Jun 2004

                  Re: user entered values

                  Ok, now I see why it doesn't work. Hard to explain, but I'll try:

                  That form is the "non JavaScript" one. For it to work you have to press the Save button which has the Domino @Function code behind it (remember the trick with only calling FileCloseWindow if no errors?). If you bypass this button by calling form.submit() in the address bar the form automatically does a save/close. At this point the WQS event runs and sets the SaveOptions field to "0". At the same time, the $$Return field tries to open the document in read mode even though it never saved in the first place. Hence the 404.

                  To submit the form normally you would have to look in the code and see what the Save button does. In this case it sets the __Click field (which tells Domino which button code to run) to

                  86256DBF007684CF.2da619e8dfc6148880256dbf006df57c/$Body/0.CF6

                  So, you could change your address bar hack to:

                  javascript: document.forms[0].__Click.value= '86256DBF007684CF.2da619e8dfc6148880256dbf006df57c/$Body/0.CF6' ;document.forms[0].submit();

                  But this would just be a normal submit and you will see the error message.

                  Et voila. You can't hack this one ;o)

                  Jake

                  This reply is already 8 levels deep and so replying at this level is disabled.

                    • avatar
                    • veer
                    • Wed 30 Jun 2004

                    Re: user entered values

                    Whoa!! Appreciate your explanation Jake. Thanks.

                1. Re: user entered values

                  if we want to see the details of employee after submitting the form and that in validation means if we entered the name.we forgot to enter the adddress so that name should be visible in the previous form after submitting the form.

                  This reply is already 8 levels deep and so replying at this level is disabled.

          • avatar
          • Jake
          • Tue 29 Jun 2004

          Article updated

          1. Re: Article updated

            Long time reader - first time poster.

            I never bothered learning JS for validation - have always used this technique. Now that you point out the JavaScript in the address bar - yikes !

            Anyway, I place all of the code in the Submit button. No agents, no WQS.

            I use one or more CFD errormessage fields and the formula includes @Return. If an error condition is found, then @Return, else do nothing (fall through to the next statements which save and redirect). @Return will reopen the document with the error message displayed but will not save the doc. SaveOptions is not necessary.

            Below is sample formula:

            t1 := @If(Name = ""; "Name"; ""); t2 := @If(City = ""; "City"; ""); t3 := @If(State = ""; "State"; ""); t4 := @If(Email = ""; "Email"; ""); tall := @Trim(t1 : t2 : t3 : t4);

            @If(tall = ""; ""; @Return(@SetField("ErrorMessage"; "Please supply a value for: " + @Implode(tall; ", ") + ".")));

            @Command([FileSave]); @URLOpen("/" + @WebDbName + "/Profiles?OpenView" )

        • avatar
        • Jilaxzone
        • Tue 28 Feb 2006

        Help Me!!

        Hi Jake! I'm newbie here.. Wanna ask you about something like the one you made. Your made app is for entering new customer on the web, isnt it?

        Btw, i've a task to be done, i have to make a web app to have user login and check their username & password. My login form's task is to check them in the domino view and let them pass if their data is in the view, how can it be??Can you help me?? What kind of connection i should make?

  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 »