面试必问系列之重定向和转发及区别

勤奋不是嘴上说说而已,而是实际的行动,在勤奋的苦度中持之以恒,永不退却。业精于勤,荒于嬉;行成于思,毁于随。在人生的仕途上,我们毫不迟疑地选择勤奋,她是几乎于世界上一切成就的催产婆。只要我们拥着勤奋去思考,拥着勤奋的手去耕耘,用抱勤奋的心去对待工作,浪迹红尘而坚韧不拔,那么,我们的生命就会绽放火花,让人生的时光更加的闪亮而精彩。

导读:本篇文章讲解 面试必问系列之重定向和转发及区别,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

概述

面试必问题其一,先看看源码。

重定向是向客户端发送一个指定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。

区别

  1. 定义不同
    请求转发(Forward):发生在服务端程序内部,当服务器端收到一个客户端的请求之后,会先将请求,转发给目标地址,再将目标地址返回的结果转发给客户端。而客户端浏览器对于这一切毫无感知的。
    请求重定向(Redirect):请求重定向指的是服务器端接收到客户端的请求之后,会给客户端返回一个302响应状态码和临时响应头,这个临时响应头中记录客户端需要再次发送请求(重定向)的 URL 地址,客户端再收到地址之后,会将请求发送到新的地址上。
  2. 请求方不同
    请求转发是服务器端的行为,服务器端代替客户端发送请求,并将结果返回给客户端;而请求重定向是浏览器客户端的行为,它们的交互流程:
    在这里插入图片描述
  3. 数据共享
    请求转发是服务器端实现的,所以整个执行流程中,客户端(浏览器端)只需要发送一次请求,因此整个交互过程中使用的都是同一个 Request和Response对象,所以整个请求过程中,请求和返回的数据是共享的;而请求重定向客户端发送两次完全不同的请求,所以两次请求中的数据是不同的。
    forward:转发页面和转发到的页面可以共享request里面的数据
    redirect:不能共享数据
  4. 地址栏(最终URL地址)
    forward:服务器行为。服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器,浏览器不知道服务器发送的内容从哪里来的,跳转过程是在服务器实现的,客户端不知道这个跳转动作,地址栏不变
    redirect:客户端行为。服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址,地址栏显示的是新的URL,历史回退按钮可点击
  5. 使用场景
    forward:一般用于用户登陆的时候,根据角色转发到相应的模块
    redirect:一般用于用户注销登陆时,返回主页面和跳转到其它的网站等
  6. 效率
    forward:高
    redirect:低
  7. 实现代码不同
    重定向跳转后必须加上return,要不然页面虽然跳转,但还会执行跳转后面的语句,转发是执行跳转页面。

总结

  • 重定向会显示真实路径,转发不会;
  • 重定向有2次浏览器请求,原来的request中的数据(第一次请求的)都丢掉,转发的request中的数据依然保留;
  • 重定向可以访问自己web应用以外的资源,转发的路径必须是同一个web容器下的URL,其不能转向到其他的web应用的路径上去,中间传递的是自己的容器内的request;请求转发只能将请求转发给同一个WEB应用中的组件,而重定向还可以重新定向到同一站点不同应用程序中的资源,甚至可以定向到一绝对的URL;

参考

关于重定向和转发
请求转发和请求重定向区别
请求转发Forward和重定向Redirect的区别

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/142186.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!