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.
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.
Reply
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?
Reply
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
Reply
Show the rest of this thread
@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/
Reply
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.
Reply
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
Reply
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
Reply
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.
Reply
Show the rest of this thread
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
Reply
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
Reply
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
Reply
Even easier server side validation
See: http://www.notesninjas.com/#InputValidation
Reply
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)")))
Reply
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")) );
Reply
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?
Reply
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+
Reply
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.
Reply
Show the rest of this thread
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)!
Reply
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
Reply
Show the rest of this thread
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?
Reply
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.
Reply