Handling duplicate form submission / Post-Redirect-Get Pattern in Spring MVC

In webapp form submission, if you refresh the form success view, most browsers will prompt a pop-up dialog to confirm about the form resubmission. If you click “yes”, the form will be resubmitted again, this scenario is well-known as duplicated form submission.

To avoid this problem, PRG pattern is used, instead of returning a web page directly, the POST operation returns a redirection command. The HTTP 1.1 specification introduced the HTTP 303 response code to ensure that in this situation, the web user’s browser can safely refresh the server response without causing the initial HTTP POST request to be resubmitted.

What is Post-Redirect-Get Pattern?

Post/Redirect/Get (PRG) is a web development design pattern that prevents duplicate form submission. When a web form is submitted to a server through an HTTP POST request, a web user that attempts to refresh the server response in certain user agents can cause the contents of the original HTTP POST request to be resubmitted, possibly causing undesired results, such as a duplicate web purchase.

STEP 1:- Open Eclipse and Create Dynamic Web Project named SpringMVCFormSubmitPRG

STEP 2:- Make sure you use Target Runtime as Apache Tomcat 7.0.

STEP 3:- copy below jars to WEB-INF/lib folder.
  • commons-logging-1.2.jar
  • hibernate-validator-4.3.1.Final.jar
  • jboss-logging-3.1.0.GA.jar
  • spring-aop-4.1.4.RELEASE.jar
  • spring-beans-4.1.4.RELEASE.jar
  • spring-context-4.1.4.RELEASE.jar
  • spring-core-4.1.4.RELEASE.jar
  • spring-expression-4.1.4.RELEASE.jar
  • spring-security-config-4.0.2.RELEASE.jar
  • spring-security-core-4.0.2.RELEASE.jar
  • spring-security-taglibs-4.0.2.RELEASE.jar
  • spring-security-web-4.0.2.RELEASE.jar
  • spring-web-4.1.4.RELEASE.jar
  • spring-webmvc-4.1.4.RELEASE.jar
  • validation-api-1.0.0.GA.jar
STEP 4:- Create Spring Configuration Bean file. /WebContent/WEB-INF/dispatcher-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="
 http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans.xsd
 http://www.springframework.org/schema/mvc 
 http://www.springframework.org/schema/mvc/spring-mvc.xsd
 http://www.springframework.org/schema/context 
 http://www.springframework.org/schema/context/spring-context.xsd">
 
 <context:component-scan base-package="com.tutorialsdesk.controller" />
 
 <mvc:annotation-driven />
 
 <bean id="viewResolver"
 class="org.springframework.web.servlet.view.UrlBasedViewResolver">
 <property name="viewClass"
 value="org.springframework.web.servlet.view.JstlView" />
 <property name="prefix" value="/WEB-INF/views/" />
 <property name="suffix" value=".jsp" />
 </bean>
 
 <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
 <property name="basename" value="/WEB-INF/messages" />
 </bean>
 
 <!-- <context:property-placeholder location="/WEB-INF/conf/messages.properties"/> -->
 
</beans>

STEP 5 :- Map Spring configuration files in /WebContent/WEB-INF/web.xml file as below :-
<?xml version="1.0" encoding="UTF-8"?>
<web-app 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-app_3_0.xsd" version="3.0">
 <display-name>SpringMVCFormSubmitPRG</display-name>
 <welcome-file-list>
 <welcome-file>login.html</welcome-file>
 </welcome-file-list>
 <servlet>
 <servlet-name>dispatcher</servlet-name>
 <servlet-class>
 org.springframework.web.servlet.DispatcherServlet
 </servlet-class>
 <load-on-startup>1</load-on-startup>
 </servlet>
 <servlet-mapping>
 <servlet-name>dispatcher</servlet-name>
 <url-pattern>*.html</url-pattern>
 </servlet-mapping>
</web-app>

STEP 6 :- create messages.propreties in /WebContent/WEB-INF/ folder as below :-
NotEmpty.userForm.email=Please enter your e-mail. 
Email.userForm.email=Your e-mail is incorrect.

STEP 7 :- Create Model Class.
  • Package: com.tutorialsdesk.model
  • Filename: User.java
package com.tutorialsdesk.model;

import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;

public class User {

 @NotEmpty
 @Email
 private String email;
 
 private int token;
 
 @NotEmpty(message = "Please enter your password.")
 @Size(min = 6, max = 15, message = "Your password must between 6 and 15 characters")
 private String password;
 
 public String getEmail() {
 return email;
 }
 
 public void setEmail(String email) {
 this.email = email;
 }
 
 public String getPassword() {
 return password;
 }
 
 public void setPassword(String password) {
 this.password = password;
 }

 public int getToken() {
 return token;
 }

 public void setToken(int token) {
 this.token = token;
 }

}

STEP 8 :- Create Controller Class.
  • Package: com.tutorialsdesk.controller
  • Filename: FormController.java
package com.tutorialsdesk.controller;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;

import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.support.RequestContextUtils;
import org.springframework.web.servlet.view.RedirectView;

import com.tutorialsdesk.model.User;

@Controller
public class FormController {
 
 @RequestMapping(value = "/login", method = RequestMethod.GET)
 public String defaultView(HttpServletRequest request, Map<String, Object> model,@ModelAttribute User userForm) {

 Map<String, ?> inputFlashMap = RequestContextUtils.getInputFlashMap(request);

 // A RedirectAttributes model is empty when the method is called and is
 // never used unless the method returns a redirect view name or a
 // RedirectView.
 if (inputFlashMap != null) {
 Boolean hasError = (Boolean) inputFlashMap.get("hasError");
 model.put("userForm", (User)inputFlashMap.get("userForm"));
 if(hasError!=null && Boolean.TRUE){
 return "LoginForm";
 }else{
 return "LoginSuccess";
 }
 }
 else{
 User user = new User();
 model.put("userForm", user);
 return "LoginForm";
 }
 }

 @RequestMapping(value = "/handle", method = RequestMethod.POST)
 public RedirectView handlePost(@Valid @ModelAttribute("userForm") User userForm,
 BindingResult result,HttpServletRequest request, 
 RedirectAttributes redirectAttrs) {
 
 redirectAttrs.addFlashAttribute("userForm",userForm);
 if(result.hasErrors()){
 redirectAttrs.addFlashAttribute("hasError",true);
 redirectAttrs.addFlashAttribute("org.springframework.validation.BindingResult.userForm", result);
 return new RedirectView("/login.html", true);
 }
 return new RedirectView("/login.html", true);
 }
}

STEP 9 :- Create jsp files in /WebContent/WEB-INF/views folder
  • Filename: LoginForm.jsp
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
 
<!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=UTF-8">
<title>Login</title>
<style>
 .error {
 color: red; font-weight: bold;
 }
</style>
</head>
<body>
 <div align="center">
 <h2>Spring MVC Preventing Duplicate Form Submissions using PRG (POST / REDIRECT / GET) Pattern</h2>
 <table border="0" width="90%">
 
 <form:form action="/SpringMVCFormSubmitPRG/handle.html" commandName="userForm">
 <tr>
 <td align="left" width="20%">Email: </td>
 <td align="left" width="40%"><form:input path="email" size="30"/></td>
 <td align="left"><form:errors path="email" cssClass="error"/></td>
 </tr>
 <tr>
 <td>Password: </td>
 <td><form:password path="password" size="30"/></td>
 <td><form:errors path="password" cssClass="error"/></td>
 </tr>
 <tr>
 <td></td>
 <td align="center"><input type="submit" value="Login"/></td>
 <td></td>
 </tr>
 </form:form>
 </table>
 </div>
</body>
</html>

  • Filename: LoginSuccess.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
 pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Welcome</title>
</head>
<body>
 <div align="center">
 <h2>Welcome ${userForm.email}! You have logged in successfully.</h2>
 </div>
</body>
</html>

STEP 10 :- Run your project enter below URL in your browser

http://localhost:8080/SpringMVCFormSubmitPRG/

Keep visiting TutorialsDesk for more tutorials and practical programming examples on Spring MVC. Hope we are able to explain you Handling duplicate form submission / Post-Redirect-Get Pattern in Spring MVC Example, if you have any questions or suggestions please write to us using contact us form.

Please share us on social media if you like the tutorial.