【问题标题】:SpringMVC controller: how to stay on page if form validation error occursSpringMVC 控制器:如果发生表单验证错误,如何留在页面上
【发布时间】:2013-08-10 19:59:30
【问题描述】:

我的 SpringMVC 控制器中有下一个工作代码:

@RequestMapping(value = "/register", method = RequestMethod.GET)
public void registerForm(Model model) {
    model.addAttribute("registerInfo", new UserRegistrationForm());
}

@RequestMapping(value = "/reg", method = RequestMethod.POST)
public String create(
        @Valid @ModelAttribute("registerInfo") UserRegistrationForm userRegistrationForm,
        BindingResult result) {

    if (result.hasErrors()) {
        return "register";
    }
    userService.addUser(userRegistrationForm);
    return "redirect:/";
}

简而言之,create 方法尝试验证 UserRegistrationForm。如果表单有错误,它会让用户在同一页面上填写表单字段,其中将显示错误消息。

现在我需要将相同的行为应用到另一个页面,但这里有一个问题:

@RequestMapping(value = "/buy/{buyId}", method = RequestMethod.GET)
public String buyGet(HttpServletRequest request, Model model, @PathVariable long buyId) {
    model.addAttribute("buyForm", new BuyForm());
    return "/buy";
}

@RequestMapping(value = "/buy/{buyId}", method = RequestMethod.POST)
public String buyPost(@PathVariable long buyId,
                          @Valid @ModelAttribute("buyForm") BuyForm buyForm,
                          BindingResult result) {

    if (result.hasErrors()) {
        return "/buy/" + buyId;
    }

    buyForm.setId(buyId);
    buyService.buy(buyForm);
    return "redirect:/show/" + buyId;
}

我遇到了动态网址的问题。现在,如果表单有错误,我应该指定相同的页面模板留在当前页面上,但我也应该将buyId 作为路径变量传递。这两个要求哪里有冲突。如果我保留此代码,我会收到一个错误(我使用 Thymeleaf 作为模板处理器):

Error resolving template "/buy/3", template might not exist or might not be accessible by any of the configured Template Resolvers

我可以写return "redirect:/buy/" + buyId 之类的东西,但在这种情况下,我会丢失表单对象的所有数据和错误。

我应该怎么做才能在buyPost 方法中实现与create 方法中相同的行为?

【问题讨论】:

  • 看看stackoverflow.com/questions/18039064/…,您可以使用 FlashAttributes 将数据传递给重定向视图。
  • 这个解决方案比重定向更好,因为表单字段的值被保存了。但在这种情况下,我为此表单丢失了BindingResult,因此在表单提交后我仍然无法向用户显示验证错误。
  • 您可以将 BindingResult 与其他数据一起传递,请参阅stackoverflow.com/questions/2543797/…
  • 谢谢,现在可以了。以前我尝试使用FlashAttributes 传递BindingResult 对象,但我无法通过@ModelAttribute 在我的buyGet 方法中获取它,因为BindingResult 不包含默认构造函数并且无法实例化。所以我通过会话传递了这个对象,看起来有点棘手。
  • Hippoom,您可以将您的 cmets 格式化为答案,我会接受它作为正确答案。

标签: validation spring-mvc


【解决方案1】:

您可以将您的 POST 实现更改为:

@RequestMapping(value = "/buy/{buyId}", method = RequestMethod.POST)
public String buyPost(@PathVariable long buyId,
                          @Valid @ModelAttribute("buyForm") BuyForm buyForm,
                          BindingResult result) {

    buyForm.setId(buyId); // important to do this also in the error case, otherwise, 
                          // if the validation fails multiple times it will not work.

    if (result.hasErrors()) {
        byForm.setId(buyId);
        return "/buy/{buyId}";
    }

    buyService.buy(buyForm);
    return "redirect:/show/{buyId}";
}

如果您使用 Spring 4.3 或更高版本,您还可以使用 @PostMapping("/buy/{buyId}") 注释该方法。

【讨论】:

    【解决方案2】:

    *我正在使用Hibernate Validator APIs 来验证我的 bean。要在显示错误消息的同时保留表单数据,您需要执行以下 3 件事:

    1. 注释您的 bean(例如,@NotEmpty、@Pattern、@Length、@Email 等)

    1. 内部控制器:

      @控制器 公共类注册控制器 {

      @Autowired
      private RegistrationService registrationService;
      
      @RequestMapping(value="register.htm", method=RequestMethod.GET, params="new")
      public String showRegistrationForm(Model model) {
          if (!model.containsAttribute("employee")) {
              model.addAttribute("employee", new Employee());
          }
          return "form/registration";
      }
      
      @RequestMapping(value="register.htm", method=RequestMethod.POST)
      public String register(@Valid @ModelAttribute("employee") Employee employee, BindingResult bindingResult, RedirectAttributes redirectAttributes) {
          if (bindingResult.hasErrors()) {
              redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.employee", bindingResult);
              redirectAttributes.addFlashAttribute("employee", employee);
              return "redirect:register.htm?new";
          }
          registrationService.save(employee);
          return "workspace";
      }
      // ....
      

      }

    2. 更新您的视图/jsp 以保存错误消息:

    这个article 肯定会有所帮助。

    【讨论】:

    • 虽然此链接可能会回答问题,但最好包含答案here 的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接的答案可能会失效。
    • 链接坏了:(
    【解决方案3】:

    我在这个周末尝试了post 中提到的解决方案,但它不适用于 BindingResult。

    下面的代码可以工作,但并不完美。

    @ModelAttribute("command")
    public PlaceOrderCommand command() {
        return new PlaceOrderCommand();
    }
    
    @RequestMapping(value = "/placeOrder", method = RequestMethod.GET)
    public String placeOrder(
            @ModelAttribute("command") PlaceOrderCommand command,
            ModelMap modelMap) {
        modelMap.put(BindingResult.MODEL_KEY_PREFIX + "command",
                modelMap.get("errors"));
        return "placeOrder";
    }
    
    @RequestMapping(value = "/placeOrder", method = RequestMethod.POST)
    public String placeOrder(
            @Valid @ModelAttribute("command") PlaceOrderCommand command,
            final BindingResult bindingResult, Model model,
            final RedirectAttributes redirectAttributes) {
        if (bindingResult.hasErrors()) {
            redirectAttributes.addFlashAttribute("errors", bindingResult);
    
            //it doesn't work when passing this          
            //redirectAttributes.addFlashAttribute(BindingResult.MODEL_KEY_PREFIX + "command", bindingResult);
    
            redirectAttributes.addFlashAttribute("command", command);
            return "redirect:/booking/placeOrder";
        }
        ......
    }
    

    【讨论】:

    • 通过addFlashAttribute 传递BindingResult 看起来像常见问题stackoverflow.com/questions/15903238/… 无论如何都找到了解决方法,因此可以关闭问题。谢谢。
    • 提到的解决方案对我也不起作用,直到我将“org.springframework.validation.BindingResult.register”中的寄存器更改为模型属性的名称。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-06
    相关资源
    最近更新 更多