Spring - POST 后重定向(即使有验证错误)

Posted

技术标签:

【中文标题】Spring - POST 后重定向(即使有验证错误)【英文标题】:Spring - Redirect after POST (even with validation errors) 【发布时间】:2011-02-02 09:09:33 【问题描述】:

我正在尝试弄清楚如何“保留” 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";

【问题讨论】:

【参考方案1】:

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

model.addAttribute("binding",binding);

我认为您可能必须使用转发而不是重定向(在我的脑海中,我不记得重定向是否会丢失会话 - 我可能错了,因为我手头没有任何文档,即,如果您在将 BindingResult 添加到模型后没有得到它,请尝试使用转发来确认这一点。

【讨论】:

我将在今天晚些时候尝试一下,但如果我转发它已经可以正常工作了。当我重定向时,我正试图让它工作。 明白了,我只是在添加 BindingResult 之后你实际上看不到它时才建议转发。【参考方案2】:

问题是您正在重定向到新控制器,而不是呈现视图并返回已处理的表单页面。您需要按照以下方式做一些事情:

String FORM_VIEW = wherever_your_form_page_resides

...

if (binding.hasErrors())
    return FORM_VIEW;

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

【讨论】:

重点是我想做重定向并且仍然显示表单错误。我知道如果我直接转发到视图会很好。 @Lewis:谢谢伙计。这个解决方案帮助我解决了我的问题。我有一个返回项目列表的“帖子”,当单击单个行项目时,用户被重定向到不同的页面(详细信息)。我想从详细信息页面重定向回上一页。如您所述,我在控制器中添加了一个 GET 并将先前处理的模型传递给初始视图。【参考方案3】:

在请求之间保持对象(即重定向)的唯一方法是将对象存储在会话属性中。因此,您将在两种方法(即 get 和 post)的方法参数中包含“HttpServletRequest request”,并通过 request.getAttribute("binding") 检索对象。也就是说,如果您自己没有尝试过,您可能需要弄清楚如何将绑定重新绑定到新请求中的对象。

另一种“不太好”的方法是使用 javascript 更改浏览器 URL

【讨论】:

您提出的方式很有趣,但是我认为如果您可以包含一些示例代码会更好【参考方案4】:

我会质疑您为什么需要重定向。为什么不直接提交到同一个 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 name 'xxxx' 的 BindingResult 和普通目标对象都不能用作请求属性” 您的 JSP 中很可能有错误。 mkyong.com/spring-mvc/… 作为我的观点,将所有属性添加到会话中不好(除非有必要)。 FlashAttributes 可以使用一次,所以我更喜欢使用它而不是会话。但这取决于您的开发方面。当我使用单个 jsp 提交表单并在不使用 JavaScript 的情况下显示成功或失败消息时,我使用了 FlashAttributes。【参考方案5】:

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

对于 BindingResult,您将使用名称:“org.springframework.validation.BindingResult.[name of your 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";

【讨论】:

绝对值得注意的是,RedirectAttributes 不能与(过时的?)ModelAndView 概念结合使用。他们假设你的方法返回一个简单的字符串,就像上面的例子一样。 在此之后(在我的情况下)该属性现在位于处理重定向的控制器模型中,但绑定结果没有错误。我必须传递错误列表并将它们添加到绑定结果中,如下所示:gist.github.com/OscarRyz/6381954 执行此操作时出现错误:java.io.NotSerializableException: org.springframework.format.support.DefaultFormattingConversionService @Jens NotSerializableException 终于在 Spring 4.3.4 中得到修复(只用了 5 年 :))jira.spring.io/browse/SPR-8282【参考方案6】:

除了 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/

【讨论】:

这为我解决了。链接的文章解释得更好。谢谢。 常见陷阱:将“org.springframework.validation.BindingResult.register”中的“register”更改为您的modelAttribute的名称 这是更正确的做法!!!!!!!!!我没有看到这个答案,并且正在努力解决@Oscar 的答案,一个建议,对于 oscar,请将此答案添加到您的答案中,以便您的答案是完整的,没有人会像我一样挣扎。 :) 谢谢大家节省了我的时间。 更好的方法是将对象创建保留在 JSP 文件中。像这样:&lt;% if( request.getAttribute("register") == null ) reqest.setAttribute("register", new Register()) %&gt;。如果您有很多控制器来解决该视图,您不想将此逻辑放入每个控制器中。尽管 JSP 中的 Java 代码被认为是不好的做法,但该代码不是业务逻辑,实际上属于表单标签。 如果您无法访问 gerrydevstory.com 上的源文章,这里有一个存档版本:web.archive.org/web/20160606223639/https://gerrydevstory.com/…【参考方案7】:

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

【讨论】:

以上是关于Spring - POST 后重定向(即使有验证错误)的主要内容,如果未能解决你的问题,请参考以下文章

会话超时后重定向(Grails、Spring Security Core、Tomcat)

使用 jquery 在 POST 请求后重定向

Laravel Fortify 登录后重定向,但不登录用户

POST django rest 框架后重定向

如何在 Django 2 中的 POST 方法后重定向到成功

django,身份验证后重定向到ajax操作