【问题标题】:Spring 3 MVC Handle multiple form submit with a single ControllerSpring 3 MVC 使用单个控制器处理多个表单提交
【发布时间】:2011-06-18 12:25:08
【问题描述】:

Spring 3 MVC 使用控制器处理多个表单提交。

我正在开发具有多种形式的 JSP 页面。 1) 搜索客户,2) 搜索产品,3) 打印某些东西等。我有一个不同的表单绑定对象绑定到每个表单,我的控制器代码如下所示

  @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;
    }
  }

上述方法肯定不像我想象的那样有效。我收到异常说'不支持请求方法'POST''。如果我在上面的控制器中只有一个 POST 方法说 searchProducts 它运行良好。但它不会使用超过一种 POST 方法。我还尝试在 JSP 中添加隐藏参数并更改类似于下面的方法签名,只是为了再次获得相同的异常。

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

任何人都可以提出正确的方法来实现上述目标吗?此外,对源材料或进一步阅读的任何参考将不胜感激。谢谢。

编辑 #1: 只要您在 JSP 中正确获取隐藏参数,上述带有 params="pageAction=searchProduct" 的方法就可以完美地工作(请参阅下面的评论)。除此之外,@Bozho 和@Biju Kunjummen 的回答也非常有帮助,并且是处理多个表单提交的好(可能更好?)替代方案。

【问题讨论】:

  • 您确定使用隐藏字段的方法不起作用吗?
  • 确实如此。我很困惑为什么它不起作用。我在这里找到了类似的问题,并使用相同的 params="name=value" 技术进行了回答。许多人投了赞成票,这一定是解决这个问题的一种方法。
  • 很奇怪,它应该可以工作。
  • @axtavt 搞定了。这是一个非常明显的错误,我想把自己埋在耻辱中。我的隐藏参数被定义为 它应该是 。你表达的怀疑让我一次又一次地重复它,直到它最终击中我。我没有足够的特权来投票给您,但非常感谢您的帮助。谢谢。

标签: spring-mvc jakarta-ee controller


【解决方案1】:

您的映射不完全正确@TMan:

  1. web.xml 中的映射是为了让 Spring Dispatcher servlet 处理您的请求 - 例如。就像你的情况一样:

    <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 结尾的 URI 请求都将由 Spring 的 DispatcherServlet 处理

  2. 控制器可以有一个映射,就像你的情况一样:

    @控制器 @RequestMapping(value="/search.do")

但是你出错的地方是控制器的 RequestMapping :

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

RequestMapping 末尾的 .do 不应该存在,因为它纯粹是为了调用 Dispatcher servlet,一旦被调用,它将处理分派到正确的 Controller 方法,因此您对 /search.do 的调用最终会得到控制器。

  1. 现在,每个方法都可以像您的情况一样使用 RequestMapping 进行注释,使用 RequestMapping 的 RequestMethod 属性,指定在 POST 或 GET 的情况下是否分派到该方法:

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

因此,当 /search.do URI 有 POST 时,将调用适当的方法。

在您的情况下,有多个方法使用 RequestMethod.POST 属性注释,因此 Spring 的 AnnotationMethodHandlerAdapter 组件根本不知道要分派到哪个方法。

您可以做以下几件事:

  1. 为每个方法放置一个请求映射,@Bozho 建议的方式: @RequestMapping(value="/customers" 方法=Request.POST) @RequestMapping(value="/products" 方法=Request.POST) 所以现在你的请求 URI 将是

    /search/customers.do /search/products.do 你应该做 POST 以获得正确的调度方法。

  2. 一起摆脱 method=Request.POST 并像上面那样依赖 @RequestMapping 来找到正确的方法。

  3. 您可以将可选的 params 属性传递给 RequestMapping,这也是帮助 Spring 找到正确的调度方法的一种方式:

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

在请求中包含客户参数或在请求中说出产品参数。

不过,最简单的是选项 1。

编辑 1:添加对良好 Spring 文档的引用 - http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html

【讨论】:

  • 嗯.. 感谢您提供非常详细和有用的解释。它确实消除了一些困惑。不幸的是,我仍然面临同样的困境。我有一个名为search.jsp 的JSP 映射到URL > search.do。此页面包含三个表单,根据您提交的表单,您会在同一页面上看到不同的结果,即 search.jsp > search.do。我从@Bozho 和您的回复中学习,并尝试将我的表单提交到不同的 URL,例如search/customer(我认为不正确)和 search/customer.do(应该工作吗?)但 tomcat 一直在抱怨“未找到请求的资源”。我被难住了。
  • 404 不适用于控制器,我猜这是现在的视图,您是否验证了视图配置正确 - 例如。
  • 是的,我有与您描述的类似的配置,不同之处在于我的视图解析器 bean 使用 UrlBasedViewResolver 类。这会是一个问题吗?我已经用 params="pageAction=searchProduct" 方法解决了我的问题,但是用这个流行的 url 技巧找出我做错了什么会非常方便。
  • 好点(3)与隐藏表单字段相结合:我正在寻找这样的解决方案。
【解决方案2】:

当您有多个表单时,为什么不将这三个表单映射到不同的 URL?

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

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

让你的表单动作指向/search/products/search/customers(不需要.do

【讨论】:

  • 谢谢。我以前试过这个,但tomcat一直抱怨“请求的资源不可用”。我确信问题出在 web.xml 中,其中我的 servlet URL 映射仅为 *.do。我正在为此制定正确的配置。任何来自您头脑中的建议都会非常方便。干杯。
  • @TMan - 啊,如果您将调度程序 servlet 映射到 *.do,那么它将无法工作。另一种方式是将其映射到/,这样一切都由spring处理。
  • 感谢 Bozho,我已经尝试过这种方法,但有些东西对我不起作用。虽然我找到了不同的工作解决方案,但我仍在调查这种方法哪里出了问题。如果我也得到这个技巧,我会更新问题文本。干杯。
【解决方案3】:

我也遇到过同样的问题:同一页面上有两个表单(注册和联系),带有两个提交按钮。 每个表单都有自己的 Spring 验证。

我已经设法在一个控制器中做到这一点:

@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 ---
}

我需要创建一个新表单(联系人或注册)并在验证失败时将其放入模型中,因为“contactOrRegister”视图需要显示两个表单。因此,当“联系”表单提交并出现错误时,“注册”表单内容将被删除。这种方式适合我。

contactOrRegister.jsp 包含具有不同操作的两种表单:

 <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>

【讨论】:

    【解决方案4】:

    你可以写

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

    在同一控制器上使用多种方法时。当用户有时想使用 GET 提交数据或您使用来自另一个控制器的 return "forward:url"; 时,这很有用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-03-08
      • 2015-05-06
      • 1970-01-01
      • 2017-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多