概述
面试必问题其一,先看看源码。
重定向是向客户端发送一个指定URL的临时重定向的响应:
public interface HttpServletResponse extends ServletResponse {
/**
* 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.
*/
void sendRedirect(String location) throws IOException;
}
转发,则是将一个请求转到服务器的另一个资源。在处理完初步请求另外的资源之后生成响应:
public interface RequestDispatcher {
/**
* 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.
*/
void forward(ServletRequest request, ServletResponse response);
}
定义基本说明转发操作为何可以保持request内的parameter,attribute等数据,而重定向操作却会丢弃的原因:
- 转发是在服务端完成的,并没有经过客户端
- 转发整个操作完成后才生成响应
- 重定向是服务端向客户端发送指定的URL
- 重定向是在客户端完成的
在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和对应的url
setHeader("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();
requestedSessionId = null;
requestedSessionURL = false;
// 重定向不能够保留数据的真正原因
parameterMap.clear();
pathParameters.clear();
}
用于存储setAttribute方法的和setParameter方法设置的数据在这里都clear。
forward方法:request.getRequestDispatcher("/hello.jsp").forward(request, response);
,内部最终会调用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,整个重定向是服务端内部进行的,浏览器并没有感知到,因此也不会显示出来。
redirect:
是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址。地址栏显示的是新的URL。
区别
- 定义不同
请求转发(Forward):发生在服务端程序内部,当服务器端收到一个客户端的请求之后,会先将请求,转发给目标地址,再将目标地址返回的结果转发给客户端。而客户端浏览器对于这一切毫无感知的。
请求重定向(Redirect):请求重定向指的是服务器端接收到客户端的请求之后,会给客户端返回一个302响应状态码和临时响应头,这个临时响应头中记录客户端需要再次发送请求(重定向)的 URL 地址,客户端再收到地址之后,会将请求发送到新的地址上。 - 请求方不同
请求转发是服务器端的行为,服务器端代替客户端发送请求,并将结果返回给客户端;而请求重定向是浏览器客户端的行为,它们的交互流程:
- 数据共享
请求转发是服务器端实现的,所以整个执行流程中,客户端(浏览器端)只需要发送一次请求,因此整个交互过程中使用的都是同一个 Request和Response对象,所以整个请求过程中,请求和返回的数据是共享的;而请求重定向客户端发送两次完全不同的请求,所以两次请求中的数据是不同的。
forward:转发页面和转发到的页面可以共享request里面的数据
redirect:不能共享数据 - 地址栏(最终URL地址)
forward:服务器行为。服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器,浏览器不知道服务器发送的内容从哪里来的,跳转过程是在服务器实现的,客户端不知道这个跳转动作,地址栏不变
redirect:客户端行为。服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址,地址栏显示的是新的URL,历史回退按钮可点击 - 使用场景
forward:一般用于用户登陆的时候,根据角色转发到相应的模块
redirect:一般用于用户注销登陆时,返回主页面和跳转到其它的网站等 - 效率
forward:高
redirect:低 - 实现代码不同
重定向跳转后必须加上return,要不然页面虽然跳转,但还会执行跳转后面的语句,转发是执行跳转页面。
总结
- 重定向会显示真实路径,转发不会;
- 重定向有2次浏览器请求,原来的request中的数据(第一次请求的)都丢掉,转发的request中的数据依然保留;
- 重定向可以访问自己web应用以外的资源,转发的路径必须是同一个web容器下的URL,其不能转向到其他的web应用的路径上去,中间传递的是自己的容器内的request;请求转发只能将请求转发给同一个WEB应用中的组件,而重定向还可以重新定向到同一站点不同应用程序中的资源,甚至可以定向到一绝对的URL;
参考
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/142186.html