一、edirect和forward的定义:
1.redirect:服务端发送给客户端一个重定向的临时响应头,这个响应头包含重定向之后的URL,客户端用新的URL重新向服务器发送一个请求。
2.forward:服务器程序内部请求转向,这个特性允许前一个程序用于处理请求,而后一个程序用来返回响应。
定义说明转发操作可以保持request内的parameter,attribute这些值都可以保留,而重定向操作却会丢弃:第一:转发实在服务端完成,并没有经过客户端;第二:转发整个操作完成后才生成响应;第三:重定向是服务端向客户端发送指定的URL;第四:重定向实在客户端完成的。
二、sendRedirect
1.代码分析:
public void sendRedirect(String location) throws IOException { if (isCommitted()) throw new IllegalStateException (sm.getString("coyoteResponse.sendRedirect.ise")); // Ignore any call from an included servlet if (included) return; // Clear any data content that has been buffered resetBuffer(); // Generate a temporary redirect to the specified location try { String absolute = toAbsolute(location); setStatus(SC_FOUND); setHeader("Location", absolute); } catch (IllegalArgumentException e) { setStatus(SC_NOT_FOUND); } // Cause the response to be finished (from the application perspective) setSuspended(true); }
方法:先把相对路径转换成绝对路径,再包装一个包含有新的URL的临时响应头,“SC_FOUND”的值是302,
就是重定向临时响应头的状态码。如果传入的“location”值不合法,就包装一个404的响应头。浏览器最终再发起新的请求,最终展现在浏览器中的即为新请求的URL。这些并不能造成重定向操作将之前request中已经绑定的一系列parameter和attribute丢掉,最根本的原因是一个请求完整处理完成之后,整个请求会有一个release的过程,即CoyoteAdapter的service方法执行完的finally块中执行release这一过程,如下:
finally { if (!comet && !async || error.get()) { request.recycle(); //注意这两行代码 response.recycle(); } }
request中recycle方法:
/** * Release all object references, and initialize instance variables, in preparation for reuse of this object. */ public void recycle() { attributes.clear(); requestedSessionId = null; requestedSessionURL = false; parameterMap.clear(); pathParameters.clear(); }
用于存储setAttribute()方法设置的和setParameter()方法设置的数据在这里都clear清理了。
2.参数
redirect在用户的浏览器端工作,sendRedirect()可以带参数传递,比如没有Test?sid=asd传至下个页面,同时它可以重定向至不同的主机上,且在浏览器地址栏上会出现重定向页面的URL。
public void sendRedirect(java.long.String location) throws java.io.IOException
这个方法将响应定向到参数location指定的、新的URL。location可以是一个绝对的URL,也可以使用相对的URL。如果location以“/”开头,则容器认为相对于当前web应用的根,否则,容器将解析为相对于当前请求的URL。这种重定向的方法,将导致客户端浏览器的请求URL跳转。从浏览器中的地址栏中可以看到新的URL地址。
三、forward
1.代码分析
request.getRequestDispatcher("/hello.jsp").forward(request,response);
forward方法内部最终会调用dispatcher的doForward方法
void doForward(ServletRequest request, ServletResponse response){ // Set up to handle the specified request and response State state = new State(request, response, false); wrapResponse(state); ApplicationHttpRequest wrequest = (ApplicationHttpRequest) wrapRequest(state); String contextPath = context.getPath(); HttpServletRequest hrequest = state.hrequest; if (hrequest.getAttribute( RequestDispatcher.FORWARD_REQUEST_URI) == null) { wrequest.setAttribute(RequestDispatcher.FORWARD_PATH_INFO,hrequest.getPathInfo()); wrequest.setAttribute(RequestDispatcher.FORWARD_QUERY_STRING, hrequest.getQueryString());} wrequest.setContextPath(contextPath); wrequest.setRequestURI(requestURI); wrequest.setServletPath(servletPath); wrequest.setPathInfo(pathInfo); if (queryString != null) { wrequest.setQueryString(queryString); wrequest.setQueryParams(queryString); } processRequest(request,response,state); //进行第二个资源的请求 } }
第二个资源的请求处理与一般的请求处理类似,只是在第一个请求之上,并没有返回响应时继续发起第二个请求,此时第一个请求的各类参数会继续向后传递,最终数据全部处理完成之后,整个响应发送回客户端。
2.参数
RequestDispatcher.forward()实在服务器端起作用,当使用forward()时,Servletengine传递HTTP请求从当前Servlet orJSP到另一个Servlet,JSP或普通HTML文件,也即form提交至a.jsp,在a.jsp用到了forward()重定向至b.jsp,此时form提交的所有信息在b.jsp都可以获得,参数自动传递,但forward()无法重定向至有frame的jsp文件,可以重定向至有frame的html文件,同时forward()无法在后面带参数传递,比如myTest()?sid=45345,这样不行,可以程序内通过response.setAttribute("sid","45345")来传至下一个页面,转发后浏览器地址栏URL不变。
四、如何选择
RequestDispatcher.forward()方法和HttpServletResponse.sendRedirect()方法的区别是:前者仅是容器中控制权的转向,在客户端浏览器地址栏中不会显示出转向后的地址;后者则是完全的跳转,浏览器将会得到跳转的地址,并重新发送请求链接。这样,从浏览器的地址栏中可以看到跳转后的链接地址。所以,前者更加高效,在前者可以满足需要时,尽量使用RequestDispatcher.forward()方法,并且这样也有助于隐藏实际的链接。在有些情况下,需要跳转到一个其它服务器上的资源,则必须使用HttpServletResponse.sendRedirect()方法。