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

Reply via email to