春季-POST后重定向(即使存在验证错误)


68

我试图弄清楚如何“保留” BindingResult,以便可以通过Spring<form:errors>标记在随后的GET中使用它。我要这样做的原因是由于Google App Engine的SSL限制。我有一个通过HTTP显示的表单,该帖子发布到HTTPS URL。如果我仅转发而不是重定向,则用户将看到https://whatever.appspot.com/my/form URL。我正在努力避免这种情况。任何想法如何解决这个问题?

以下是我想做的事情,但是使用时我只会看到验证错误return "create"

@RequestMapping(value = "/submit", method = RequestMethod.POST)
public final String submit(
    @ModelAttribute("register") @Valid final Register register,
    final BindingResult binding) {

    if (binding.hasErrors()) {
        return "redirect:/register/create";
    }

    return "redirect:/register/success";
}

Answers:


75

从Spring 3.1开始,您可以使用RedirectAttributes。在进行重定向之前,添加您希望具有的属性。添加BindingResult和用于验证的对象,在本例中为Register。

对于BindingResult,您将使用名称:“ org.springframework.validation.BindingResult。[ModelAttribute的名称]”。

对于用于验证的对象,将使用ModelAttribute的名称。

要使用RedirectAttributes,必须将其添加到配置文件中。您要告诉Spring使用一些较新的类,其中包括:

<mvc:annotation-driven />

现在,无论您将重定向到哪里,都将显示错误

@RequestMapping(value = "/submit", method = RequestMethod.POST)
public final String submit(@ModelAttribute("register") @Valid final Register register, final BindingResult binding, RedirectAttributes attr, HttpSession session) {

if (binding.hasErrors()) {
    attr.addFlashAttribute("org.springframework.validation.BindingResult.register", binding);
    attr.addFlashAttribute("register", register);
    return "redirect:/register/create";
}

return "redirect:/register/success";
}

4
绝对值得注意的是,RedirectAttributes不能与(过时的)ModelAndView概念一起使用。他们假定您的方法返回一个简单的字符串,如上面的示例所示。
kevinmrohr

此后(以我为例),该属性现在位于处理重定向的控制器模型中,但是绑定结果没有错误。我必须传递错误列表并将其添加到绑定结果中,如下所示:gist.github.com/OscarRyz/6381954
OscarRyz

2
这样做时出现错误:java.io.NotSerializableException:org.springframework.format.support.DefaultFormattingConversionService
Jens

@Jens NotSerializableException终于在春季4.3.4中修复(只花了5年:))jira.spring.io/browse/SPR-8282
zutnop

62

除了Oscar的好答案之外,如果您采用这种RedirectAttributes方法,请不要忘记您实际上是将传递modelAttribute给了重定向页面。这意味着,如果为重定向页面(在控制器中)创建该modelAttribute的新实例,则会丢失验证错误。因此,如果您的POST控制器方法是这样的:

@RequestMapping(value = "/submit", method = RequestMethod.POST)
public final String submit(@ModelAttribute("register") @Valid final Register register, final BindingResult binding, RedirectAttributes attr, HttpSession session) {

if (binding.hasErrors()) {
    attr.addFlashAttribute("org.springframework.validation.BindingResult.register", binding);
    attr.addFlashAttribute("register", register);
    return "redirect:/register/create";
}

return "redirect:/register/success";
}

然后,您可能需要在注册创建页面GET控制器中进行修改。由此:

@RequestMapping(value = "/register/create", method = RequestMethod.GET)
public String registerCreatePage(Model model) {
    // some stuff
    model.addAttribute("register", new Register());
    // some more stuff
}

@RequestMapping(value = "/register/create", method = RequestMethod.GET)
public String registerCreatePage(Model model) {
    // some stuff
    if (!model.containsAttribute("register")) {
        model.addAttribute("register", new Register());
    }
    // some more stuff
}

资料来源:http : //gerrydevstory.com/2013/07/11/preserving-validation-error-messages-on-spring-mvc-form-post-redirect-get/


这为我解决了。链接的文章对此进行了更好的解释。谢谢。
adnans 2014年

6
常见陷阱:将“ org.springframework.validation.BindingResult.register”中的“ register”更改为您的modelAttribute的名称
borjab 2014年

1
这是更正确的方法!我没有看到这个答案,并且正在为@Oscar答案而苦恼,这是对奥斯卡的一个建议,请将这个答案添加到您的答案中,这样您的答案将是完整的,而且没有人会像我一样挣扎。:)谢谢大家节省了我的时间。
Rupesh Yadav。

更好的方法是将对象创建保留在JSP文件中。像这样:<% if( request.getAttribute("register") == null ) reqest.setAttribute("register", new Register()) %>。如果您有很多控制器可以解决该视图,那么您就不想在每个控制器中都加入这种逻辑。而且,尽管JSP中的Java代码被认为是不好的做法,但该代码不是业务逻辑,实际上属于form标记。
UTF_or_Death '16

6
如果您无法访问gerrydevstory.com上的源文章,请使用以下存档版本:web.archive.org/web/20160606223639/https
//gerrydevstory.com/…– maxxyme 2016年

2

我会问你为什么需要重定向。为什么不只是提交到相同的URL,然后对POST做出不同的响应?不过,如果您确实要这样做:

@RequestMapping(value = "/submit", method = RequestMethod.POST)
public final String submit(
    @ModelAttribute("register") @Valid final Register register,
    final BindingResult binding,
    HttpSession session) {

    if (binding.hasErrors()) {
        session.setAttribute("register",register);
        session.setAttribute("binding",binding);
        return "redirect:/register/create";
    }

    return "redirect:/register/success";
}

然后在您的“创建”方法中:

model.put("register",session.getAttribute("register"));
model.put("org.springframework.validation.BindingResult.register",session.getAttribute("register"));

尝试过您的方法,将引发异常:“ Bean名称'xxxx'的BindingResult或普通目标对象均不可作为请求属性”
user979051 2013年

您的JSP中最有可能是错误。 mkyong.com/spring-mvc/…–
rjsang

作为我的观点,将所有属性添加到会话很糟糕(除非必要)。FlashAttributes只能使用一次,所以我宁愿使用它而不是会话。但这取决于您的发展方面。当我使用单个jsp进行表单提交并显示成功或失败的消息而不使用JavaScript时,我会使用FlashAttributes。
Menuka Ishan

1

问题是您要重定向到新的控制器,而不是呈现视图并返回已处理的表单页面。您需要执行以下操作:

String FORM_VIEW = wherever_your_form_page_resides

...

if (binding.hasErrors())
    return FORM_VIEW;

由于字符串的代码重复,我会将路径保留在任何方法之外。


2
整个问题是我想执行重定向并仍然显示表单错误。我知道,如果我直接转发视图,它将很好用。
泰勒·里斯

@刘易斯:谢谢你。此解决方案帮助我解决了问题。我有一个``帖子''返回项目列表,当单击单个订单项时,用户将重定向到另一个页面(详细信息)。我想从详细信息页面重定向回上一页。如您所述,我在控制器中添加了GET并将先前处理的模型传递给初始视图。
yonikawa '16

1

在请求之间持久保存对象的唯一方法(即重定向)是将对象存储在会话属性中。因此,您将在两个方法的方法参数中都包含“ HttpServletRequest请求”(即,获取和发布),并通过request.getAttribute(“ binding”)检索对象。话虽如此,并且您自己还没有尝试过,您可能需要弄清楚如何在新请求中将绑定重新绑定到对象。

另一种“更简单”的方法是仅使用javascript更改浏览器URL


1
你提出的方式很有趣,但我认为它甚至会更好,如果你可以包括一些示例代码
晃龙

0

我不知道Google App Engine的确切问题,但是使用ForwardedHeaderFilter可能有助于保留客户端使用的原始方案。该过滤器是在Spring Framework 4.3中添加的,但是某些Servlet容器提供了类似的过滤器,并且该过滤器是自给自足的,因此您也可以根据需要获取源代码。


-1

也许这有点简单,但是您是否尝试过将其添加到模型中?即,将Model包含在方法的参数中,然后将BindingResult添加到其中,然后在视图中可用。

model.addAttribute("binding",binding);

我认为您可能必须使用转发而不是重定向(在我的脑海中,我不记得重定向是否丢失了会话-我对此可能是错误的,因为我没有任何方便的文档,即您将BindingResult添加到模型后没有得到,请尝试使用Forward来确认)。


我将在今天晚些时候尝试一下,但是如果我转发,它已经可以正常工作。我正在尝试使其在重定向时正常工作。
Taylor Leese 2010年

理解了,我只是建议转发,如果在添加BindingResult之后您实际上没有看到它。
Furusato一郎
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.