Wednesday, May 9, 2012

How to avoid Duplicate submission using Struts Token with code example


There are many scenarios where we face duplicate submission problem like,

  • Using Refresh button
  • Using browser back button traverse back and re-submitting the form
  • Using browser history feature and re-submit the form and so on
To avoid this problem Struts provide 'Struts Token' concept.

How does it work? 

  1. saveToken generates a unique token and saves it in the session under the key org.apache.struts.action.TOKEN. 
  2. When the form is rendered, the struts html:form tag generates a hidden field named org.apache.struts.action.TOKEN. 
  3. Upon form submission, isTokenValid compares the token stored in the session with that submitted from the form. If they are equal, return true. Otherwise, return false.
  4. resetToken removes the token stored in the session. 

Step by step procedure to implement using Token in struts,

Step 1 :
Save the token in session using 'saveToken()' method which is implemented in Action class.


public class EmployeeLoadAction extends Action{
private final static String SUCCESS = "success";
@Override
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
ActionForward forward;
forward = mapping.findForward(SUCCESS);
saveToken(request);
return forward;
}
}


Step 2 :
In JSP file. Here in this example employee.jsp
Store the token using Hidden variable in JSP file as shown below.
Don't forget to import Globals and Constants class inside JSP file to avoid Jasper Exception

TRANSACTION_TOKEN_KEY variable inside Action class is deprecated, so use it from Globlas class instead.

<%@ page import="org.apache.struts.Globals"%> 
<%@ page import="org.apache.struts.taglib.html.Constants"%> 
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<form name="employee" action="EmployeeSubmit.do" method="POST">
<input type="hidden" name="<%= Constants.TOKEN_KEY %>" 
value="<%= session.getAttribute(Globals.TRANSACTION_TOKEN_KEY) %>" > 
<TABLE>
<TR>
<TD>Name</TD>
<TD><input type="text" name="empName"></TD>
</TR>
<TR>
<TD>ID</TD>
<TD><input type="text" name="empId"></TD>
</TR>
<TR>
<TD colspan="2"><input type="submit" value="Submit"></TD>
</TR>
</TABLE>
</form>
</body>
</html>


Step 3 :

Now actual logic for duplicate submission inside EmployeeSubmit action class.
Method 'isTokenValid()' will validate the token and returns the boolean. 
Based on that we can decide whether form has re-submitted.


public class EmployeeSubmitAction extends Action{
private final static String SUCCESS = "success";
@Override
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
ActionForward forward;
forward = mapping.findForward(SUCCESS);
EmployeeSubmitForm frm = (EmployeeSubmitForm) form;
if (isTokenValid(request)) {
System.out.println("frm.getName() : " + frm.getEmpName());
resetToken(request);
} else {
System.out.println("frm.getName() : " + frm.getEmpName());
System.out.println("Duplicate Submission of the form");
}
return forward;
}
}


9 comments:

  1. It's good tutorial and given simple, understanding level.
    Thanks

    ReplyDelete
  2. Short & precise tutorial, but no offence kindly correct the method name of isTokenValid() in step 3. It'll create confusion in beginners.

    ReplyDelete
    Replies
    1. @Ronak:
      Updated and thanks a ton for the observation.

      Delete
  3. thank you its good to understand the concept.

    ReplyDelete
  4. I am getting the error :Cannot cast from ActionForm to EmployeeSubmitForm. please reply .. it very urgent

    ReplyDelete
    Replies
    1. Please check if your class "EmployeeSubmitForm" is extending "ActionForm". i.e public EmployeeSubmitForm extends ActionForm.

      Delete
  5. how to configure these two action classes(i.e EmployeeSubmitAction ,EmployeeLoadAction ) in struts-config.xml and what is the url for this application

    ReplyDelete