Spring 3 MVC Handle multiple forms with one controller

Spring 3 MVC Handle multiple forms with a controller.

I am developing a JSP page with several forms. 1) Customer search, 2) Product search, 3) Printing something, etc. I have another form binding object attached to each form, and my controller code is similar to below

@Controller @RequestMapping(value="/search.do") public class SearchController { @RequestMapping(method = RequestMethod.GET) public String pageLoad(ModelMap modelMap) { modelMap.addAttribute("productSearch", new ProductSearchCriteria()); modelMap.addAttribute("customerSearch", new CustomerSearchCriteria()); modelMap.addAttribute("print", new PrintForm()); } @RequestMapping(method = RequestMethod.POST) public ModelAndView searchProducts(@ModelAttribute("productSearch") ProductSearchCriteria productSearchCriteria, BindingResult result, SessionStatus status) { //Do Product search return modelAndView; } @RequestMapping(method = RequestMethod.POST) public ModelAndView searchCustomers(@ModelAttribute("customerSearch") CustomerSearchCriteria customerSearchCriteria, BindingResult result, SessionStatus status) { //Do Customer search return modelAndView; } @RequestMapping(method = RequestMethod.POST) public ModelAndView printSomething(@ModelAttribute("print") PrintForm printForm, BindingResult result, SessionStatus status) { //Print something return modelAndView; } } 

The above, of course, does not work, as I expected. I get an exception saying "The request method" POST "is not supported . " If I have only one POST method inside the controller, let's say searchProducts it works well. But with POST there will be no more than one method. I also tried adding a hidden parameter to the JSP and changing the method signatures like below, just to get the same exception again.

  @RequestMapping(method = RequestMethod.POST, params="pageAction=searchProduct") public ModelAndView searchProducts(@ModelAttribute("productSearch") ProductSearchCriteria productSearchCriteria, BindingResult result, SessionStatus status) { //Do Product search return modelAndView; } 

Can anyone suggest the right way to achieve the above? Any reference to the source material or further reading will be greatly appreciated. Thanks.

EDIT # 1: The above approach with parameters = "pageAction = searchProduct" works fine as far as you can get your hidden parameter directly in JSP (see comment below). In addition to this, the answers @Bozho and @Biju Kunjummen are also very helpful and are a good (maybe better?) Alternative to resolving multiple submit forms.

+6
java-ee spring-mvc controller
source share
4 answers

Your mappings are not entirely correct @TMan:

  • The mappings in web.xml should receive the Spring dispatcher servlet to process your request - for example. as in your case:

     <servlet> <servlet-name>appServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:/META-INF/appServlet/servlet-context.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> 

    appServlet * .do So, now any URI request ending in .do will be handled by Spring DispatcherServlet

  • The controller may have a mapping, for example, in your case:

    @Controller @RequestMapping (value = "/search.do")

But where you got it wrong, this is in RequestMapping for the controller:

 @Controller @RequestMapping(value="/search") 

.do at the end of RequestMapping should not be there, since it is just for the dispatcher servlet that is being called, after it is called, it processes sending the correct controller method, so your /search.do call will end with the controller.

  1. Now each method can be annotated using RequestMapping, as in your case, with the RequestMethod attribute RequestMapping, indicating whether to send the method in case of POST or GET:

     @RequestMapping(method = RequestMethod.GET) @RequestMapping(method = RequestMethod.POST) 

So, when there is POST in the URI / search.do, the corresponding method will be called.

In your case, there are several methods annotated with the RequestMethod.POST attribute, so the AnnotationMethodHandlerAdapter Spring component simply does not know which method to send to.

There are a few things you can do:

  • puts a matching query for each method, as @Bozho suggests: @RequestMapping (value = "/ customers" method = Request.POST) @RequestMapping (value = "/ products" method = Request.POST) so now your request URI will be

    /search/customers.do /search/products.do and you must do a POST to get the right submit method.

  • Get rid of the method = Request.POST all together and depend on @RequestMapping as above to find the correct method.

  • You can pass the optional params attribute to RequestMapping, which again helps Spring find the right method to send:

    @RequestMapping (method = Request.POST, params = "clients")

with customer parameters in the request or product parameters in the request.

The simplest option would be option 1.

EDIT 1: Adding a link to a good Spring document - http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html

+11
source share

If you have multiple forms, why don't you match the three forms with different URLs?

 @RequestMapping("/products") public ModelAndView searchProducts(..) @RequestMapping("/customers") public ModelAndView searchCustomers(..) 

And your actions on the form are pointed to /search/products and /search/customers (no need for .do )

+5
source share

I had the same problem: two forms (registration and contact) on the same page with two submit buttons. Each form has its own Spring validation.

I managed to do this in one controller:

 @RequestMapping(value = "/contactOrRegister.do", method = RequestMethod.GET) public void contactOrRegisterFormGet(@ModelAttribute("contactForm") ContactForm contactForm, @ModelAttribute("registerForm") RegisterForm registerForm) { //-----prepare forms for displaying here---- } @RequestMapping(value = "/contact.do", method = RequestMethod.POST) public String contactFormPost(@ModelAttribute("contactForm") ContactForm contactForm, BindingResult result, final Model model) { contactValidator.validate(contactForm, result); if (result.hasErrors()) { model.addAttribute("registerForm", new RegisterForm()); return "contactOrRegister"; } //--- validation is passed, do submit for contact form here --- } @RequestMapping(value = "/register.do", method = RequestMethod.POST) public String registerFormPost(@ModelAttribute("registerForm") RegisterForm registerForm, BindingResult result, final Model model) { registerValidator.validate(registerForm, result); if (result.hasErrors()) { model.addAttribute("contactForm", new ContactForm()); return "contactOrRegister"; } //--- validation is passed, do submit for register form here --- } 

I need to create a new form (contact or register) and put it in the model if the check is not completed, because to view "contactOrRegister" you need both forms to display. Therefore, when the contact form was submitted and there are errors, the contents of the register form will be erased. This method suits me.

Contact OrRegister.jsp contains both forms with different actions:

  <form:form action="/register.do" modelAttribute="registerForm" method="POST"> <!-- register form here --> </form:form> <form:form action="/contact.do" modelAttribute="contactForm" method="POST"> <!-- contact form here --> </form:form> 
+5
source share

You can write

@RequestMapping(method = {RequestMethod.GET,RequestMethod.POST})

when using several methods on one controller. This is useful when sometimes the user wants to send data using GET or when using return "forward:url"; from another controller.

+2
source share

All Articles