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
Hide the rest of this thread
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?
Reply
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
Reply
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?
Reply
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
Reply
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();
Reply
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.
Re: user entered values
Whoa!! Appreciate your explanation Jake. Thanks.
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.
Article updated
Reply
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" )
Reply
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?
Reply
@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