Spring 跨重定向请求转递数据

2020/11/13 21

Spring

在处理完 POST 请求后,一个最佳实践就是执行一下重定向。除了其它因素外,最主要的是能够防止用户点击浏览器刷新按钮或后退箭头时,客户端重新执行危险的 POST 请求。

重定向前的方法如何发送数据给重定向后的方法呢?一般来讲,当一个处理器方法完成之后,请求会转发(forward)到视图上进行渲染。因为控制器方法和视图所处理的是同一个请求,所以在转发的过程中,请求属性得以保存。

但是,当控制器的结果是重定向的话,原始的请求就结束了,浏览器会重新发一个新的 GET 请求,原始请求中所带有的模型数据也就随着第一个请求一起消亡了。

通过 URL 模板进行重定向

return "redirect:/spitter/" + spitter.getUsername();

上述代码其实就是通过 URL 保存了值,在有些时候,它确实很完美,但并不是说它没有问题,它很容易被篡改。如果用户传入的是危险字符串,会破坏程序,因此需要这样改写程序:

@RequestMapping(value = "/register", method = RequestMethod.POST)
public String processRegistration(Spitter spitter, Model model) {
    spitterRepository.save(spitter);
    model.addAttribute("username", spitter.getUsername());
    model.addAttribute("id", spitter.getId());
    return "redicrect:/spitter/{username}";
}

这样,username 作为占位符填充到 URL 模板中,所以 username 中所有的不安全字符都会被转义。同时,id 没有匹配 URL 中的任何占位符,所以它会自动以 QueryString 的形式附加到 URL 上。

通过 URL 模板进行重定向只能发送交单的值,并没有办法处理更复杂的值。

使用 flash 属性

假设我们不想在重定向中发送 username 和 id 了,而是要发送实际的对象。如果只发送 id 的话,还需要从数据库中再次查询才能得到对象。但是在重定向之前,我们已经得到了 Spitter 对象,为什么不把它发送给重定向的方法,并将其展现出来呢?

有个方案是将 Spitter 放到 Session 中,Session 能够长期存在,并且能够跨请求。实际上,Spring 也认为将跨重定向存活的数据放到 Session 中是一个不错的方式。但是,Spring 认为我们并不需要管理这些数据,Spring 提供了将数据发送为 flash 属性的功能。按照定义,flash 属性会一直携带这些数据直到下一次请求,然后才会消亡。

读到这里,内心有一丝颤抖,这不就是 .NET MVC 中的 TempData 吗?

@RequestMapping(value = "/register", method = RequestMethod.POST)
public String processRegistration(Spitter spitter, RedirectAttributes model) {
    model.addAttribute("username", spitter.getUsername());
    model.addFlashAttribute("spitter", spitter);
    return "redicrect:/spitter/{username}";
}

没有设置 key 参数,因此会被自动推断,推断结果是 spitter。