关于重定向和转发,书上和老师都没告诉你的......

 

说起重定向和转发,搞Web开发的都说的头头是道。你真了解其本质有什么区别么?为什么一个可以保留参数,一个却留不住呢?应用服务器内部是怎么处理的呢?...



说起重定向,转发,每个Web开发者估计都能头头是道的说上几句类似于重定向会显示真实路径,转发不会以及其它一类教科书式的概念。这些概念也都没错,但却没从本质上说明这两者的区别。更别提其根本原理是怎么实现的,以及为何重定向之后,原来的request中的数据都丢掉了,而转发却还是能保证request中的数据依然保留呢?

作为一个有追求的程序员,希望本文可以让你深入了解这两者的本质区别,从而知其所以然。

  1. 定义
首先来看两者的javadoc。

sendRedirect()

/**
*
Sends a temporary redirect response to the client using the
specified redirect location URL.
This method can accept relative URLs; the servlet container must convert the relative URL to an absolute URL before sending the response to the client. If the location is relative without a leading '/' the container interprets it as relative to the current request URI.
If the location is relative with a leading '/' the container interprets it as relative to the servlet container root.
*/
public void sendRedirect(String location) throws IOException;

重定向是向客户端发送一个指定URL的临时重定向的响应。

forward()

/**
*
Forwards a request from a servlet to another resource (servlet, JSP file, or HTML file) on the server. This method allows one servlet to do preliminary processing of a request and another resource to generate the response.The request and response parameters must be either the same objects as were passed to the calling servlet's service method .
*/
public void forward(ServletRequest request, ServletResponse response);

转发,则是将一个请求转到服务器的另一个资源。在处理完初步请求另外的资源之后生成响应。

定义基本说明了转发操作为何可以保持request内的parameter,attribute这些值都可以保留,而重定向操作却会丢弃的原因:

  • 转发是在服务端完成的,并没有经过客户端
  • 转发整个操作完成后才生成响应
  • 重定向是服务端向客户端发送指定的URL
  • 重定向是在客户端完成的
我们再来看Tomcat内部,对于两者是怎样一种实现方式。

2. 容器实现

我们在servlet内部一般对于这两者的使用形式也相当直观,例如对于hello.jsp的请求:

sendRedirct方法

response.sendRedirect("/hello.jsp");此时,内部的处理方式如下:
public void sendRedirect(String location, int status) throws IOException {

// Generate a temporary redirect to the specified location

try {

String absolute = toAbsolute(location);

setStatus(status); //这里,重定向是返回302状态码以及Location和对应的urlsetHeader("Location", absolute);

} catch (IllegalArgumentException e) {

setStatus(SC_NOT_FOUND);

}}
展现在浏览器中的结果如下:


即根据Location,浏览器最终再发起新的请求,最终展现在浏览器中的即为新请求的URL,也就是大家常说的重定向会显示最终的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();[/b]

requestedSessionId = null;

requestedSessionURL = false;

parameterMap.clear();

pathParameters.clear();[/b]
}我们看到用于存储setAttribute方法设置的和setParameter方法设置的数据在这里都clear掉了。这也是重定向不能够保留数据的真正原因。
forward方法forward方法一般使用如下:
request.getRequestDispatcher([b]"/hello.jsp"
).forward(request, response);[/b]
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); //进行第二个资源的请求

}
}第二个资源的请求处理与一般的请求处理类似,只是在第一个请求之上,并没有返回响应时继续发起第二个请求,此时第一个请求的各类参数会继续向后传递,最终数据全部处理完成之后,整个响应发送回客户端。此时上面的release流程也依然会走,但并没有什么影响,毕竟第二个资源已经请求处理完成。

而由于浏览器发请求的时候是一个固定的URL,整个重定向是服务端内部进行的,浏览器并没有感知到,因此也不会显示出来。

整个应用服务器内部处理过程再加上清晰的定义,相信这两者的本质区别已经显露无疑。

如果感觉本文有用,就让更多朋友看到它吧。

「Tomcat那些事儿」文章深入Tomcat源码,分析应用服务器的实现细节,工作原理。以及与之相关的技术,使用技巧,工作实战等。起于Tomcat,但不止于此。同时会分析分享JVM、并发等,内容多为原创,欢迎关注。 交流或讨论可以加QQ群:96437267


    关注 Tomcat那些事儿


微信扫一扫关注公众号

0 个评论

要回复文章请先登录注册