先描述一個場景:
你到一個機關辦事,一個是辦事窗口的那個人很不客氣地說,這個事你別找我,你找xxx窗口,然后你自己跑到xxx窗口,那個窗口的人直接給你辦了;還有一個是窗口的人和你說,你等下,他自己跑去找另一個人溝通一番,然后跑回來給你辦了。
前者是redirect重定向,后者便是forward轉發。
重定向redirect和轉發forward的流程圖如下:
(1)客戶端跳轉--重定向redirect
客戶端向服務器發送請求時,服務器返回一個“去訪問其他鏈接”的響應,客戶端根據此響應,第二次去訪問服務器,服務器給出最終的響應,所以總共有兩次請求。客戶端跳轉時,地址欄會發生改變。
使用response的rsendRedirect方法:
重定向格式:response.sendRedirect("path");
(2)服務端跳轉--轉發forward
客戶端向服務器發送請求時,服務器發現當前資源給不出回應,服務器需要在內部請求另一個資源的跳轉,然后給出響應,這屬於1次請求,由於服務器跳轉與否客戶端並不知道,所以地址欄的url並不會改變。
使用requestDispatcher對象:
轉發格式:request.getRequestDispatcher("path").forward(response,request)
使用jsp動作元素:
<jsp:forward page=""/>
(3)轉發與重定向的區別
1.地址欄
轉發:不變,不會顯示出轉向的地址
重定向:會顯示轉向之后的地址
2.請求
轉發:一次請求
重定向:至少提交了兩次請求
3.數據
轉發:對request對象的信息不會丟失,因此可以在多個頁面交互過程中實現請求數據的共享
重定向:request信息將丟失
4.原理
請求轉發為服務器內部跳轉,跳轉一次,客戶端接收結果,而不改變url地址,而請求重定向則跳轉兩次,既將結果返回給客戶端,又使客戶端的url地址改變。
請求轉發為為內部跳轉,頁面請求的對象一直存在,請求重定向則會結束上個頁面的請求。
請求轉發的傳參使用request對象方法setAttribute(“name”,value),請求重定向只需使用url傳參即可。
(4)實例:
例子1:
轉發:在返回值前面加"forward:"
從第一個轉發到其他的處理器,是不需要寫傳遞的數據的,因為數據共享
重定向:在返回值前面加"redirect:
實例2:
1、常規用法,返回一個View
1 @RequestMapping(value="/testa", method=RequestMethod.GET) 2 public String inputData(){ 3 return "testa"; //Spring框架找到對應的View並渲染 4 } 5 6 @RequestMapping(value="/testa", method=RequestMethod.POST) 7 public String outputData(HttpServletRequest request){ 8 String userName = request.getParameter("name"); 9 String password = request.getParameter("pwd"); 10 request.setAttribute("name", userName); 11 request.setAttribute("pwd", password); 12 return "testb"; //Spring框架找到對應的View並渲染 13 }
打開testa網頁:
輸入用戶名:spring,密碼:spring:
點擊登陸按鈕,頁面變為如下:
再次刷新,谷歌瀏覽器提示重新提交表單。
對比圖片,發現瀏覽器的輸入框中URL不變,但是不同情況下顯示不同的View。跳轉時Model共享(表單會被重復提交)。
2、轉發(forward)
1 @RequestMapping(value="/testa", method=RequestMethod.GET) 2 public String inputData(){ 3 return "testa"; //Spring框架找到對應的View並渲染 4 } 5 6 @RequestMapping(value="/testa", method=RequestMethod.POST) 7 public String outputData(HttpServletRequest request){ 8 String userName = request.getParameter("name"); 9 String password = request.getParameter("pwd"); 10 request.setAttribute("name", userName); 11 request.setAttribute("pwd", password); 12 //轉發到 /testb 的Controller方法(即outputDataX)上 13 return "forward:/testb"; 14 } 15 16 @RequestMapping(value="/testb", method=RequestMethod.POST) 17 public String outputDataX(HttpServletRequest request){ 18 return "testb"; 19 }
打開testa網頁:
輸入用戶名:spring,密碼:spring:
點擊登陸按鈕,頁面變為如下:
調試分析:forward后面跟一個資源。當程序運行到return “forward:/testb”時,會執行會執行該資源對應的方法outputDataX。
另外轉發時,瀏覽器的URL不變。
再次刷新,谷歌瀏覽器提示重新提交表單。
3、重定向(redirect)
1 @RequestMapping(value="/testa", method=RequestMethod.GET) 2 public String inputData(){ 3 return "testa"; //Spring框架找到對應的View並渲染 4 } 5 6 @RequestMapping(value="/testa", method=RequestMethod.POST) 7 public String outputData(HttpServletRequest request){ 8 String userName = request.getParameter("name"); 9 String password = request.getParameter("pwd"); 10 request.setAttribute("name", userName); 11 request.setAttribute("pwd", password); 12 //重定向到 /testb 的Controller方法(即outputDataY)上 13 return "redirect:/testb"; 14 } 15 16 @RequestMapping(value="/testb", method=RequestMethod.POST) 17 public String outputDataX(HttpServletRequest request){ 18 return "testb"; 19 } 20 21 @RequestMapping(value="/testb", method=RequestMethod.GET) 22 public String outputDataY(HttpServletRequest request){ 23 return "testb"; 24 }
打開testa網頁:
輸入用戶名:spring,密碼:spring:
點擊登陸按鈕,頁面變為如下:
調試分析:redirect后面跟一個資源。當執行到return “redirect:/testb”時,會執行該資源對應個方法outputDataY。由於重定向Model不共享,所以頁面無數據顯示。
另外重定向后瀏覽器的輸入框中URL也發生變化。
刷新后,谷歌瀏覽器沒有提示重新提交表單
總結:
常說的可以通過redirect: URL防止重復提交表單,就是上面過程的意思。
原理是對於redirect而言,Request的attribute不會被傳遞,放到session中,session在跳到頁面后馬上移除對象。所以你刷新一下后這個值就會丟掉。
如果你希望Request的attribute被傳遞,可以使用RedirectAttributes類。
1 @RequestMapping(value="/testa", method=RequestMethod.GET) 2 public String inputData(){ 3 return "testa"; //Spring框架找到對應的View並渲染 4 } 5 6 @RequestMapping(value="/testa", method=RequestMethod.POST) 7 public String outputData(HttpServletRequest request, RedirectAttributes redirectAttributes){ 8 String userName = request.getParameter("name"); 9 String password = request.getParameter("pwd"); 10 request.setAttribute("name", userName); 11 request.setAttribute("pwd", password); 12 //重定向到 /testb 的Controller方法(即outputDataY)上 13 //重定向傳遞參數的兩種方法 14 redirectAttributes.addAttribute("name", userName); 15 redirectAttributes.addFlashAttribute("pwd", password); 16 17 return "redirect:/testb"; 18 } 19 20 @RequestMapping(value="/testb", method=RequestMethod.POST) 21 public String outputDataX(HttpServletRequest request){ 22 return "testb"; 23 } 24 25 @RequestMapping(value="/testb", method=RequestMethod.GET) 26 public String outputDataY(HttpServletRequest request){ 27 String userName = request.getParameter("name"); 28 request.setAttribute("name", userName); 29 return "testb"; 30 }
運行如下:
上面示例了使用RedirectAttributes傳遞參數的兩種方法:
1. 使用RedirectAttributes類的addAttribute方法傳遞參數會跟隨在URL后面,如上圖谷歌瀏覽器所示,URL為http://localhost:8080/testb?name=spring
2. 使用RedirectAttributes類的addFlashAttribute方法傳遞參數不會跟隨在URL后面,會把該參數值暫時保存於session,待重定向URL獲取該參數后從session中移除,這里的redirect必須是方法映射路徑,jsp無效。你會發現redirect后的jsp頁面中pwd只會出現一次,刷新后pwd再也不會出現了。下圖為刷新后的結果,密碼pwd顯示為空。這驗證了上面說的,pwd在被訪問后就會從session中移除。對於防止重復提交可以使用此方法。
參考鏈接:
https://ask.csdn.net/questions/376121
https://blog.csdn.net/jiangshangchunjiezi/article/details/88998809
https://blog.csdn.net/webzhuce/article/details/54564608