Recipe7.9.Preventing Double Form Submissions


Recipe 7.9. Preventing Double Form Submissions

Problem

You need to stop users from inadvertently submitting a form twice.

Solution

Use the Struts token facility to reject a duplicate request. First, as shown in Example 7-6, save a token in the HTTP request in the Action which precedes the JSP containing the form.

Example 7-6. Action that saves token
package com.oreilly.strutsckbk.ch06; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; public class SaveTokenAction extends Action {     public ActionForward execute(ActionMapping mapping, ActionForm form,             HttpServletRequest request, HttpServletResponse response)             throws Exception {         // save a token         saveToken(request);         // load the data to view         BusinessService.loadData( );                  return mapping.findForward("success");     } }

You don't have to make any changes to the JSP that displays the form as long as you create the form using the html:form tag (as shown in Example 7-7).

Example 7-7. JSP page for token handling
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %> <html> <head>   <title>Struts Cookbook - Chapter 7 : Token Test</title> </head> <body bgcolor="white"> <h2>Token Test</h2>   <html:errors/>   <html:form action="/SaveData">       <html:text property="name"/>       <html:submit/>   </html:form>   <html:link action="/SaveData" transaction="true">Save</html:link> </body> </html>

In the Action that processes the form, shown in Example 7-8, check the token. If the token is invalid, reject the request by throwing an exception or returning errors; otherwise, continue normal processing.

Example 7-8. Action that checks token validity
package com.oreilly.strutsckbk.ch06; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.Globals; import org.apache.struts.action.Action; import org.apache.struts.action.ActionError; import org.apache.struts.action.ActionErrors; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; public class CheckTokenAction extends Action {     public ActionForward execute(ActionMapping mapping, ActionForm form,             HttpServletRequest request, HttpServletResponse response)             throws Exception {         if (isTokenValid(request)) {             // reset the token             resetToken(request);             // save data             BusinessService.saveData( );         }         else {             ActionErrors errors = new ActionErrors( );             errors.add(ActionErrors.GLOBAL_ERROR,                         new ActionError("Invalid token"));             saveErrors(request, errors);             return new ActionForward(mapping.getInput( ));         }         return mapping.findForward("success");     } }

Discussion

Inadvertent or duplicate form submissions cause real problems with many web applications. When the form being submitted results in financial transactions, real dollars can be erroneously lost or gained. A couple of common scenarios result in double form submission. If the server's form processing takes several seconds, the user may be tempted to resubmit the form, thinking the first submit wasn't received. Users get in the habit of refreshing a page when the browser isn't responding. The typical user may not realize that pressing Refresh resubmits the form.

Another common problem is double-clicks. Users are conditioned by the operating system to use double-click to open applications. Users unaccustomed to web browsing commonly use double-click when they need to single-click. The double-click problem happens because users have a "loose trigger finger."

Struts provides a mechanism for managing these problems. This mechanism can't prevent a user from submitting a form twice (see the Sidebar 7-1), but it does allow for an Action to check if the request was received as expected. If the request wasn't expected, the action can reject the request and generate an appropriate error or exception.

The Solution shows the basic pattern for utilizing tokens. You need an Action that forwards to the JSP page containing the form. Usually this pre-form Action is responsible for loading the data to be displayed. In this Action, call the saveToken(HttpServletRequest request) method provided by the base Struts Action class (org.apache.struts.action.Action). This method generates a unique String value, as a token for the current transaction and saves that value under a known attribute name in the HTTP request.

When the form is rendered on the JSP page, Struts generates an HTML hidden field containing the token value. You can generate the token value as request parameter on a hyperlink by setting transaction=true on the html:link tag. The token value, whether rendered as a hidden field or a request parameter, is only generated if a token is found in the current request for the JSP. The generated page source from the JSP is shown in Example 7-9.

Example 7-9. Generated form with Struts token
<html> <head>   <title>Struts Cookbook - Chapter 7 : Token Test</title> </head> <body bgcolor="white"> <h2>Token Test</h2>    <form name="TestForm" method="post" action="/jsc-ch07/SaveData.do">   <input type="hidden" name="org.apache.struts.taglib.html.TOKEN"         value="8f72ef608fb385fd757513ff5fc1b091">   <input type="text" name="name" value="">   <input type="submit" value="Submit"> </form> <a href="/jsc-ch07/SaveData.do?org.apache.struts.taglib.html.  TOKEN=8f72ef608fb385fd757513ff5fc1b091">   Save Data </a>   <hr />   <a href="/jsc-ch07/index.jsp">Home</a> </body> </html>

You check the token value in the Action that is the target of the form or link using the isTokenValid( ) method of the base Struts Action. If the token is invalid, you can generate an appropriate error or throw an exception. If it is, then you should clear the token using the resetToken( ) method.

Preventing Double-Clicks Using JavaScript

JavaScript can be used to prevent a user from submitting a form twice. A JavaScript function is called as an onclick event handler. The function checks the value of a global variable indicating if the form has been submitted. If the value isn't set, the indicator is set to true and the form is submitted. If the value has been set, then the request is ignored. This solution doesn't prevent the user from refreshing the page, so you will want to use the Struts token facility on the server-side.


See Also

The methods for generating, saving, checking, and resetting tokens are all defined in the Struts Action class. JavaDocs for this class can be found at http://struts.apache.org/api/org/apache/struts/action/Action.html.

The JavaWorld online magazine has a good article on the Struts token handling and can be found at http://www.javaworld.com/javatips/jw-javatip136_p.html. The topic comes up frequently on the struts-user mailing list; search for "token" and you'll find a number of discussions.

The "Introduce Synchronizer Token" refactoring presented in the book Core J2EE Patterns by Deepak Alur, John Crupi, and Dan Malks (Sun Microsystems Press) was based, in part, on the Struts token facility.



    Jakarta Struts Cookbook
    Jakarta Struts Cookbook
    ISBN: 059600771X
    EAN: 2147483647
    Year: 2005
    Pages: 200

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net