Sunday, November 05, 2006

the web sucks

As a developer who really likes to get stuff done (in terms of business goals), I really detest writing the "client" side of webapps. Some work I did recently to help a struts application deal with the problems of duplicate submissions, back button issues, have really set me off, ready to go back to swing, or GWT, or... We (programmers) are just writing way too much plumbing to deliver basic web applications. I know this is probably old news for some of you, but embarassingly, most of the applications I have worked on to date, have simply ignored the problems. Here's my quick research, and what you can do about it.

1. Goal #1: Prevent duplicate submissions.

Note although the stated goal is to prevent duplicates, it is really more like saying "make sure the user reloads the screen before they submit". In other words, make sure they run the Http Get request before the POST of the form. Your business logic should kick in at some point in the process, but I digress.

How to fix: Put something in the emitted web (GET)page that when the form loads, it is a hidden field. Store this emitted token on the server side. Later, when the user submits the form
check that the token being submitted matches what exists on the server side.
If you are using Struts: use isValidToken(request) when the post comes in, and use
resetToken(request);
saveToken(request);
When the form is displayed.

Get fancy: you might be able to put this in a filter if you are crafty. However, if you have server side servlet.getDispatcher().forward type of code, or forward to another action in struts, you may run into trouble. This is because a servlet forward is not another request from the browser, hence subsequent chained actions may be checking tokens when they don't need to.

Problem #2
Back /forward navigation -
To me, if you have form posts, you ought to be able to simply disable back/forward in the browser, as it is meant for the "page" paradigm, viewing books and the like. In fact, some browsers(I think opera) you can't completely kill cache. Caching in any GUI intensive web app is a nightmare, simply because your server side state doesn't match what the user sees.

Anyway, seems like the best way to make this work, is a combination of tools.
1. When a form posts executes, do NOT display a page after doing the post, but instead, send a redirect to fetch a new page- presumably the "result" of their post.
See here for a decent explaination:
here and here

The impact of this appoach (in struts) seems that
a. any messages sent to the user on success, will need to be stuck in the session, and not request
b. if you have are using struts validation, and you have an error, your form will need to be in the session, as your form validation failure was a POST too, so that, too, should use a redirect.

So much for session persistence, given our new bloated session sizes. Good thing I think its overrated anyway.

If you do the above steps, you'll still have an issue: the browser will be caching perhaps when you choose back/forward. So, In order to disable that in struts, you just add some stuff to the controller xml file in the struts-config, eg:



Note: if you are on struts V1.28 or below, this WONT work on Firefox, as they need something in the http header called: no-store. Older versions of struts won't set this header.

If you would like to see your http headers BTW, check out the firefox extension, which helpeed me track this down.

http://livehttpheaders.mozdev.org/

Hopefully I'll get a chance to play with Seam or WebFlow one of these days, which take care of a lot of this junk for us.

No comments: