先描述一个场景:
你到一个机关办事,一个是办事窗口的那个人很不客气地说,这个事你别找我,你找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