Handling duplicate form submission in Spring MVC
In last Spring MVC form handling example, 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.
Figure : example of duplicated form submission.
The common solution to this is using “Post/Redirect/Get” Design Pattern. It will redirect to another URL if the form submission is successfully, instead of returning a web page directly.
Check the details explanation of Post/Redirect/Get Design Pattern in Wiki.
Post/Redirect/Get Design Pattern in Spring MVC
In this tutorial, we show you how to apply the “Post/Redirect/Get” Design Pattern in Spring MVC to solve the duplicated form submission problem in last form handling example.
1. Duplicate form submission
See below normal form declaration that will hits the duplicate form submission problem.
File : mvc-dispatcher-servlet.xml
<bean
class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" />
<bean class="com.mkyong.customer.controller.CustomerController">
<property name="formView" value="CustomerForm" />
<property name="successView" value="CustomerSuccess" />
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
In above snippet, the CustomerController
returns a “CustomerSuccess” view directly, which should be replace with a redirect URL instead.
2. Redirect View
Declared a review view, named “customerSuccessRedirect” and return an URL “CustomerSuccess.htm“.
File : spring-views.xml
<beans ...>
<!-- Redirect view -->
<bean id="customerSuccessRedirect"
class="org.springframework.web.servlet.view.RedirectView">
<property name="url" value="CustomerSuccess.htm" />
</bean>
</beans>
3. Spring Configuration
Update the mvc-dispatcher-servlet.xml settings to link all Spring’s configuration together.
- Update the “successView” to the new redirect view, named “customerSuccessRedirect“.
- Declare a “XmlViewResolver” to load the redirect view.
- Put a priority order for the “InternalResourceViewResolver” and “XmlViewResolver“, otherwise the “InternalResourceViewResolver” will always match and give your application no chance to call the “XmlViewResolver“.
- Declare a “ParameterizableViewController” controller to match the redirect URL and return a view to user. Since the “ControllerClassNameHandlerMapping” won’t generated the mapping for any build-in Spring’s controller, so you have to define the explicit mapping in “SimpleUrlHandlerMapping“.
File : mvc-dispatcher-servlet.xml
<bean
class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" />
<bean class="com.mkyong.customer.controller.CustomerController">
<property name="formView" value="CustomerForm" />
<property name="successView" value="customerSuccessRedirect" />
<!-- it was
<property name="successView" value="CustomerSuccess" />
-->
</bean>
<!-- Redirect Controller -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/CustomerSuccess.htm">customerSuccessController</prop>
</props>
</property>
</bean>
<bean id="customerSuccessController"
class="org.springframework.web.servlet.mvc.ParameterizableViewController">
<property name="viewName" value="CustomerSuccess" />
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
<property name="order" value="1" />
</bean>
<bean class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="location">
<value>/WEB-INF/spring-views.xml</value>
</property>
<property name="order" value="0" />
</bean>
4. How it works?
1. Access URL : http://localhost:8080/SpringMVC/customer.htm.
2. Fill in and submits the form.
3. Return “successView”, which is “customerSuccessRedirect“.
<bean class="com.mkyong.customer.controller.CustomerController">
<property name="formView" value="CustomerForm" />
<property name="successView" value="customerSuccessRedirect" />
</bean>
4. “XmlViewResolver” match it and return a “RedirectView” with URL “CustomerSuccess.htm“.
<bean id="customerSuccessRedirect"
class="org.springframework.web.servlet.view.RedirectView">
<property name="url" value="CustomerSuccess.htm" />
</bean>
5. “SimpleUrlHandlerMapping” match it and return a ParameterizableViewController, “customerSuccessController“, and return the view name “CustomerSuccess“.
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/CustomerSuccess.htm">customerSuccessController</prop>
</props>
</property>
</bean>
<bean id="customerSuccessController"
class="org.springframework.web.servlet.mvc.ParameterizableViewController">
<property name="viewName" value="CustomerSuccess" />
</bean>
6. “InternalResourceViewResolver” match it and return the final view “/WEB-INF/pages/CustomerSuccess.jsp“.
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
<property name="order" value="1" />
</bean>
7. URL changed to http://localhost:8080/SpringMVC/CustomerSuccess.htm.
8. Try refresh the success form page , the form resubmission dialog will not prompt anymore.
The overall concept is return a redirect URL instead of a direct page.
Hi.
I published today something similar. But only annotation is used. Please have a look and let me know there is something that I can do to make it more better. Here is the link – http://blogs.niteshapte.com/2013-07-15-how-to-prevent-duplicate-form-submission-in-spring-mvc.htm
Nice Tutorial?!!!
If i am using annotation config. can i use following logic???
@Controller
@RequestMapping(?/customers?)
public class EmployeeController
{
//in post method
@RequestMapping(method=RequestMethod.POST)
public String createCustomer(Customer customer)
{
customerDao.add(customer);
return ?redirect:/customers?;
}
@RequestMapping(method=RequestMethod.GET)
public String showCustomers(Model model)
{
List custombers = customerDao.list();
model.addAttribute(?custombers?, customers);
return ?customers/list?;
}
}
Wonderful beat ! I would like to apprentice whilst you amend your site,
how can i subscribe for a weblog website? The account aided me a applicable deal.
I had been a little bit familiar of this your
broadcast provided vivid clear concept
I really like it when people get together and share ideas.
Great site, stick with it!
Thanks for sharing this tutorial but how this can be achieved in Spring 3 with annotations somehow redirect:path doesn’t work. It would be great if we get same kind of example from you for spring 3 with annotations.
can you write example how to do this for the annotation example?
hi can you do this for the annotation example as well??? can’t get it working using redirect
Hi,
Great site.
i am facing a problem with this solution. the url is changed but form/command data is missing. please help me out on this.
I tried implementing the similar concept in my application. xmlViewResolver is able to match the corresponding redirectView. But after that SimpleUrlHandlerMapping is unable to get the proper controller i.e. customerSuccessController in this example. I have mapped everything properly but even then it is working properly. I am using spring 2.0 dtd.
Below is my configuration.
taskSuccessController
can you please help me out.
I tried implementing the similar concept in my application. xmlViewResolver is able to match the corresponding redirectView. But after that SimpleUrlHandlerMapping is unable to get the proper controller i.e. customerSuccessController in this example. I have mapped everything properly but even then it is working properly. I am using spring 2.0 dtd.
Below is my configuration.
can you please help me out.
Please wrap your code properly.
How do we achieve this using annotation ?
is it like return “redirect : success.htm”
create a controller which will be matched to success.htm and in get function , return success.jsp ?