Hello, I am currently toying with my serverside solution to the double submit problem thats a little different from traditional approaches, so I wanted to run it by whoever likes theorycrafting and see if they have any feedback.
The problem I have with traditional solutions is that on a double submit, there is usually a hard error or a page reload and the original form submission result is lost. So I wanted to come up with a way to process the form once, but show a consistent result, regardless of whether the original or a duplicate submission was processed. The solution in a nutshell is to first insert a token into the form when its rendered (in an extention of the stripes form tag). Validation code runs regardless. Then on a valid submit, I synchronize all requests on the token, and store the result of the submission in session as an object that knows how to return the appropriate resolution, whether it completed successfully or not. I created an abstract class called AbstractAction, which looks something like this (forgive pseudocode): public abstract class AbstractAction { private String formToken = UUID.randomUUID().toString; private boolean successfulAction; // subclasses process the form protected abstract boolean doAction(ActionBean actionBean); // the resolution to return if doAction completed successfully protected abstract Resolution onSuccessfulAction(ActionBean actionBean); // the resolution to return if doAction did not complete successfully protected abstract Resolution onUnsuccessfulAction(ActionBean actionBean); // the public entry point used in actionBean classes public Resolution act(ActionBean actionBean) { synchronize(formToken) { // this is simplified - in reality it requires synching a global instance of this string // look for an already completed action AbstractAction action = session.get(formToken); // if this is the first, the form is processed by the subclass one time // the action object itself is put into session if(action == null) { this.successfulAction = doAction(actionBean); session.put(formToken, this); action = this; } // the appropriate resolution is returned - duplicate submissions immediately jump to here return (action.successfulAction ? onSuccessfulAction(actionBean) : onUnsuccessfulAction(actionBean); } } } So the doAction is only called once per duplicate submission, but the resolution methods can be called multiple times. The idea is to create a subclass of this and store it in your action bean (I come from Wicket, where anonymous methods abound, but I prefer to create private classes) This is what an action bean might look like: public class PurchaseActionBean { private PurchaseAction purchaseAction; private CreditCard creditCard; public Resolution submit() { // purchaseAction is instantiated when stripes sets the form token into it from the tag return purchaseAction.act(this); } // this class now handles the form processing private static class PurchaseAction extends AbstractAction { private boolean creditCardDeclined; private long confirmationNumber; @Override protected boolean doAction(ActionBean actionBean) { Result result = orderService.placeOrder(actionBean.getCreditCard()); if(result.isCreditCardDeclined) { // processing did not complete successfully, return false here this.creditCardDeclined = true; return false; } // processing is complete, so return true this.confirmationNumber = result.getConfirmationNumber(); return true; } @Override protected Resolution onSuccessfulAction(ActionBean actionBean) { return new RedirectResolution(ConfirmationActionBean.class).addParameter(confirmationNumber); } @Override protected Resolution onUnsuccessfulAction(ActionBean actionBean) { if(creditCardDeclined) actionBean.getErrors().add("card declined"); return new ForwardResolution('wherever.jsp'); } } } You'll notice we need to store some state in the object, anything that the resolutions will need to use for different requests. But by splitting up the resolution actions into separate methods, a duplicate submission will come in, read the result of the original processing in session, and execute the resolutions again, along with any error messages or whatnot, and the response will look identical to the first one. I believe this gives a better experience to the user than showing an error about double submits or simply reloading the page and leave them wondering what happened. There is an issue of orphaned session data, my solution would be to store the formTokens in an expiration map and set them to expire after a minute or so. The lifetime of a token only needs to last from the second the first submission finishes processing to the time it takes to process all the waiting requests. That shouldn't be long since subsequent requests go immediately to the resolution methods. Another idea would be to encrypt the formToken so users can't modify it and circumvent this solution. I realize there is a lot of info there, but I'm curious to hear feedback about it. This may be overkill if client side prevention is in place, since we are essentially only worried about the user with javascript disabled and impatience. Maybe its too much of a burden to create these action classes for every form submit, and should only be done on critical forms. On the other hand, it does offer a way to protect the application and effectively hide the effects of double submits from users. I'm wrestling over implementing it everywhere or just critical forms like credit card processors. Any thoughts? -- View this message in context: http://old.nabble.com/An-alternative-solution-to-handle-double-form-submissions.-Feedback--tp32111253p32111253.html Sent from the stripes-users mailing list archive at Nabble.com. ------------------------------------------------------------------------------ 5 Ways to Improve & Secure Unified Communications Unified Communications promises greater efficiencies for business. UC can improve internal communications as well as offer faster, more efficient ways to interact with customers and streamline customer service. Learn more! http://www.accelacomm.com/jaw/sfnl/114/51426253/ _______________________________________________ Stripes-users mailing list Stripes-users@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/stripes-users