Search code examples
strutsstruts-1double-submit-problem

How to prevent resubmitting a form on refresh in Struts 1?


I have gone through the answers with same problem, but isn't there any default way in Struts to prevent resubmission? Also, in my case, if I do reset the form fields, it still saves the old values (but, how do it getting those values)?

I have an dispatch action class as:

public class QrcAction extends DispatchAction
{
    public ActionForward waiverPage( final ActionMapping inMapping,
                                     final ActionForm inForm,
                                     final HttpServletRequest inRequest,
                                     final HttpServletResponse inResponse )
    {
        QrcForm qrcForm = (QrcForm) inForm;
        if ( StringUtils.isEmpty( qrcForm.getCustomerId() ) )
        {
            ActionMessages errors = getErrors( inRequest );
            errors.add( IAppConstants.APP_ERROR, new ActionMessage( IPropertiesConstant.ERROR_INVALID_CUSTOMER_ID ) );
            saveErrors( inRequest, errors );
            return inMapping.findForward( IActionForwardConstant.QRC_SEARCH_CUSTOMER_PAGE );
        }
        qrcForm.setCustWaiverPojo( new CrmCustWaiverPojo() );
        qrcForm.setRemarksPojo( new RemarksPojo() );
        return inMapping.findForward( IActionForwardConstant.WAIVER_PAGE );
    }

    public ActionForward applyWaiver( final ActionMapping inMapping,
                                      final ActionForm inForm,
                                      final HttpServletRequest inRequest,
                                      final HttpServletResponse inResponse )
    {
        String forward = IActionForwardConstant.WAIVER_PAGE;
        QrcForm qrcForm = (QrcForm) inForm;
        ActionMessages messages = getMessages( inRequest );
        ActionMessages errors = getErrors( inRequest );
        CrmuserDetailsDto userDto = (CrmuserDetailsDto) inRequest.getSession( false )
                .getAttribute( IAppConstants.CRM_USER_OBJECT );
        
        double waiverLimit = 0;
        if ( StringUtils.isNotEmpty( userDto.getWaiverLimitAmmount() ) )
            waiverLimit = Double.parseDouble( userDto.getWaiverLimitAmmount() );
        if ( waiverLimit < qrcForm.getCustWaiverPojo().getWaiverAmount().doubleValue() )
        {
            errors.add( IAppConstants.APP_ERROR, new ActionMessage( IPropertiesConstant.ERROR_INVALID_WAIVER_LIMIT,
                                                                    waiverLimit ) );
        }
        QrcFormHelper.validateWaiver( errors, qrcForm );  // validates pojo fields

        if ( errors.isEmpty() )
        {
            // saving data to database
            if ( success )
            {
                messages.add( IAppConstants.APP_MESSAGE, new ActionMessage( "success.msg.key" ) );
                // resetting the form fields
                qrcForm.setCustWaiverPojo( new CrmCustWaiverPojo() );
                qrcForm.setRemarksPojo( new RemarksPojo() );
                qrcForm.setSrTicketNo( null );
            }
            else
                errors.add( IAppConstants.APP_ERROR, new ActionMessage( "error.msg.key" ) );
        }
        saveErrors( inRequest, errors );
        saveMessages( inRequest, messages );
        return inMapping.findForward( forward );
    }
}

The waiverPage() is called to display the form and it has submitted to applyWaiver(). In applyWaiver(), on successful saving values, I'm resetting the form fields and forwarding it to the same page. After submitting and getting a success result, a message is displayed and the form is cleared, and after that, if I do refresh it again, that makes an entry in db with the same, previously entered values.

Could there be any solution to this problem?

UPDATE:

waiver.jsp:

<html:form action="manageQrc.do?method=applyWaiver">
    <html:hidden property="custWaiverPojo.customerId" name="qrcForm" value="${ qrcForm.customerId }"/>
    <label class="label_radio"> 
        <html:radio property="custWaiverPojo.waiverType" value="Goodwill Wavier" name="qrcForm" styleClass="waiverType">Goodwill</html:radio>
    </label> 
    <label class="label_radio"> 
        <html:radio property="custWaiverPojo.waiverType" value="Process Wavier" name="qrcForm" styleClass="waiverType">Process</html:radio>
    </label>
    <strong> Select Category </strong> 
    <span class="LmsdropdownWithoutJs"> 
        <html:select property="custWaiverPojo.waiverHead" name="qrcForm" styleId="waiverHead">
            <html:option value="">Please Select</html:option> <!-- options are filled after radio button selection -->
      </html:select>
    </span>
    <strong>Amount</strong>
    <html:text property="custWaiverPojo.waiverAmount" name="qrcForm" styleClass="textbox" styleId="waiverAmount" maxlength="10"></html:text>
    <strong>Bill No. </strong> 
    <span class="LmsdropdownWithoutJs"> 
        <html:select property="custWaiverPojo.billNo" name="qrcForm" styleId="waiverBill">
            <html:option value="0">Please Select</html:option>
            <html:option value="20140101">20140101</html:option>
            <html:option value="20140201">20140201</html:option>
            <html:option value="20140301">20140301</html:option>
            <html:option value="20140401">20140401</html:option>
        </html:select>
    </span>
    <strong>Ticket ID</strong>
    <html:text property="srTicketNo" name="qrcForm" maxlength="20" styleClass="textbox" styleId="waiverSRT"></html:text>
    <strong> Remarks</strong>
    <html:textarea property="remarksPojo.remarks" name="qrcForm" styleClass="LmsRemarkstextarea" styleId="waiverRemarks"></html:textarea>
    <html:submit />
</html:form>

Solution

  • The solution in this blog uses token to identify the request as:

    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 is 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, the actual logic for duplicate submission is inside the EmployeeSubmit action class.

    The method isTokenValid() will validate the token and return 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;
        }
    }