Spring MVC 之 Flash Attribute詳解

Spring MVC 3.1 版本增添了Flash Attribute的新特性,解決了一直以來關於POST請求後跳轉頁面導致原有用戶傳入的數據無法續存的問題。

講解這個知識點前, 先要理解瀏覽器的所謂「刷新」的本質是什麼。

瀏覽器刷新的本質是將你上一次發送的HTTP請求重新發送一次。

比如你現在在url地址欄中輸入一個網址,打開了這個網址對應的頁面,按下了F5,瀏覽器會重新發送一次你的url指定的HTTP請求,但是這種刷新方式也引來了一定的問題。

我之前的一篇文章,

牛岱:五分鐘用Spring Boot+MySQL做一個登陸系統?

zhuanlan.zhihu.com圖標

中, 就遇到了這個問題。

這個例子很簡單,就是註冊賬號(POST請求,將用戶名、密碼、郵箱存到資料庫中),登陸賬號。

如圖所示:

那麼如果我現在在註冊新賬號底下填寫了必要信息, 點擊了註冊之後, 我會向伺服器發送一個POST請求, 這個POST請求即包含了url, 又包含了我們傳入了表單信息, 那麼如果我現在點擊刷新, 剛才的POST請求會再次進行一遍,也就是重新註冊一次賬號,如果Controller的方法里沒有要求郵箱不可重複的話,資料庫中就會存儲上兩個一模一樣的賬號信息,比如我現在註冊一個新賬號:

點擊了註冊後, 我刷新一下頁面, 再返回, 我會發現資料庫中有兩組相同的數據:

如何避免這種現象呢?

那就是使用Redirect方法,讓用戶提交了表單數據後,返回一個Redirect回應, 強制讓用戶側自動進行一次Get請求, 這樣當用戶再次刷新的時候, 就不會重新提交一次表單數據了, 所以我們只需再Controller方法中增加跳轉功能:

@PostMapping(path = "/add")
public String addNewUser (@RequestParam String name
, @RequestParam String email, @RequestParam String password, User user) {
user.setName(name);
user.setEmail(email);
user.setPassword(password);
userRepository.save(user);
log.info(user.toString()+" saved to the repo");
return "redirect:/";
}

我將返回值改成了redirect:/, 也就是當用戶提交了表單信息後, 該方法會返回一個redirect:/response, 強制讓用戶進行跳轉, 跳轉到入口界面, 也就是根目錄的位置, 用一個圖來解釋這個原理:

當用戶填寫了表單, 提交了數據, 服務端將數據存在數據空中, 然後發送了讓用戶跳轉的指令, 然後用戶自動用GET方法跳轉到另一個頁面, 這樣當用戶刷新的時候, 就會重新發送GET方法, 而不是之前的POST

但是這樣一來又出來一個問題, 就是我無法直接將用戶提交的表單數據顯示在跳轉後的頁面, 比如我想在用戶登陸後, 在跳轉後的頁面標題上顯示Hello, 用戶名, 但是因為Spring MVC的M層僅僅是對當前的請求有效, 你將用戶名的信息存在Model里, 然後再跳轉, 跳轉後的那個Controller方法是不會得到你上一個Model里的信息的, 這就是為什麼我們要引入Flash Attribute

Flash Attribute里保存的信息會傳入下一個跳轉頁面的Model里,或者這麼說: 對於一個Controller層的映射方法來說, 它目前儲存的Flash Attribute是來自於上一個頁面, 也就是由其跳轉來的頁面, 而在當前方法中儲存的Flash Attribute會傳入下一個即將跳轉過去的頁面里,有了它,我們便解決了這個問題。

看這個圖:

FlashMap是用來以Key-Value形式儲存Flash Attribute的API, 而FlashMapManager則是用來儲存, 獲取, 管理FlashMap實例的API。

對於剛才列出的Controller代碼,我們再做如下改動

@PostMapping(path = "/add") // Map ONLY GET REQUESTs.
public String addNewUser (@RequestParam String name
, @RequestParam String email, @RequestParam String password, User user, RedirectAttributes redirectAttributes) {
// @ResponseBody means the returned String is a response, not a view name.
user.setName(name);
user.setEmail(email);
user.setPassword(password);
userRepository.save(user);
redirectAttributes.addFlashAttribute("username", name);
log.info(user.toString()+" saved to the repo");
return "redirect:/";
}

在方法傳入參數中加一個RedirectAttributes, 然後在代碼中給redirectAttributes增加一個key="username"FlashAttribute, value為用戶在表單數據中填寫的用戶名。

然後跳轉後,我們的主頁面便會顯示:

希望大家能有所收穫。


推薦閱讀:

TAG:SpringMVC | Spring | SpringBoot |