Thursday, 10 January 2013

how to avoid re sending post response on refresh or click

i faced this problem while submitting post data , if a user clicks it again , it again gets submitted in database or if the page is refreshed. the solution for this problem is using a simple token which is generated every time user requests for the post form and checking this token again . i have used jsp , and jstl for this . this is a jsp page which will post the data to the servlet . please note here i am using a hidden input along with our custom tag .
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"

pageEncoding="ISO-8859-1"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="/WEB-INF/tld/token.tld" prefix="token"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<link rel="shortcut icon" href="/favicon.ico" />
<link rel="stylesheet" type="text/css"
href="${pageContext.request.contextPath}/css/style.css">
<title>Create Customer</title>
</head>
<body>
<form name="createUser" action="createUser" method="post"><label
for="email Id">email</label> <input type="text" name="email">
<div class="clear"></div>
<input type="hidden" name="token"
value="<token:generate/>"></input>
<label for="name">Name</label>
<input type="text" name="name">
<div class="clear"></div>

<label for="name">Address</label>
<input type="text" name="address">
<div class="clear"></div>

<label for="name">Account Type</label>
<input type="text" name="accountType">
<div class="clear"></div>

<label for="name">Openiing Balance</label>
<input type="text" name="initialBalance">
<div class="clear"></div>

<input type="submit" style="margin: -20px 0 0 287px;" class="button"
name="commit" value="Create User"></form>
<div style="color: Red">${validationError}</div>

<a href="view.jsp">back</a>

</body>
</html>
this is tag class which will pass the token value in request attribute as well as set it in session attribute .
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.Tag;

public class TokenGenerator implements Tag
{

 private PageContext pageCtx;
 private final int digits = 1000000;
 @Override
 public int doEndTag() throws JspException
 {
  // TODO Auto-generated method stub
  return 0;
 }

 @Override
 public int doStartTag() throws JspException
 {
  int randomInt = (int) (Math.random() * digits);
  try
  {
   System.out.println("in tag class" + randomInt);
   pageCtx.getOut().print(randomInt);
   System.out.println("after writing to pagectx");
   pageCtx.getSession().setAttribute("token", randomInt);
  }
  catch(IOException e)
  {
   System.out.println("in exception of tag cl;ass");
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  return 0;
 }
 @Override
 public Tag getParent()
 {
  // TODO Auto-generated method stub
  return null;
 }

 @Override
 public void release()
 {
  // TODO Auto-generated method stub

 }

 @Override
 public void setPageContext(PageContext arg0)
 {
  pageCtx = arg0;
 }

 @Override
 public void setParent(Tag arg0)
 {
  // TODO Auto-generated method stub

 }

}
Token Validator Filter will check , if the request and session has same token ,means it is coming from the form, otherwise the filter will redirect to home page .
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet Filter implementation class TokenValidator
 */
public class TokenValidator implements Filter {

 /**
  * Default constructor.
  */
 public TokenValidator() {
  // TODO Auto-generated constructor stub
 }

 /**
  * @see Filter#destroy()
  */
 public void destroy() {
  // TODO Auto-generated method stub
 }

 /**
  * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
  */
 public void doFilter(ServletRequest request1, ServletResponse response1,
   FilterChain chain) throws IOException, ServletException {
  System.out.println("token validator called");
  HttpServletRequest request = (HttpServletRequest) request1;
  HttpServletResponse response = (HttpServletResponse) response1;

  boolean validRequest = false;
  Object token1 = request.getParameter("token");
  Object token2 = request.getSession().getAttribute("token");
  
  if (token1 != null && token2 != null) {
   Integer first = Integer.parseInt(token1.toString());
   Integer second = Integer.parseInt(token2.toString());
   if (first.equals(second))
    validRequest = true;
  }

  if (!validRequest) {
   System.out.println("redirecting response");
   response.sendRedirect("view.jsp");
  } else {
   chain.doFilter(request1, response1);
  }

 }

 /**
  * @see Filter#init(FilterConfig)
  */
 public void init(FilterConfig fConfig) throws ServletException {
  // TODO Auto-generated method stub
 }

}


the tld file of token
<taglib version="2.1" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemalocation="
            http://java.sun.com/xml/ns/javaee 
            http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd">
    <tlib-version>1.0</tlib-version> 
    <short-name>MyTag</short-name>
    <uri>/WEB-INF/customTag</uri>
     
   <tag>
  <name>generate</name>
  <tag-class>TokenGenerator</tag-class>
  <body-content>empty</body-content>
  </tag>

</taglib>
Feel Free to ask questions