请求重定向和请求转发

1. 概述

在J2EE开发中,我们总是会接触到请求重定向和请求转发
重定向是指通过各种方法将网络请求重新定个方向转到其它位置,而转发是指把网页的请求重新转发到另一个站点。那么请求转发和请求重定向有什么区别,以及其实现的原理是怎么样的呢。


2. 两者的区别

(1)请求重定向是一种客户端行为,通过HttpServletResponse的对象来实现

response.sendRedirect();

HttpServletResponse.sendRedirect方法调用者与被调用者使用各自的request对象和response对象,它们属于两个独立的访问请求和响应过程。重定向不会保留上一次的请求对象,request域中的数据不能保留,地址栏的URL地址会改变。HttpServletResponse.sendRedirect方法对浏览器的请求直接作出响应,响应的结果就是告诉浏览器去重新发出对另外一个URL的访问请求。

请求重定向的过程:
客户浏览器发送http请求—-》web服务器接受后发送302状态码响应及对应新的location给客户浏览器–》客户浏览器发现是302响应,则自动再发送一个新的http请求,请求url是新的location地址—-》服务器根据此请求寻找资源并发送给客户。在这里location可以重定向到任意URL,既然是浏览器重新发出了请求,则就没有什么request传递的概念了。在客户浏览器路径栏显示的是其重定向的路径,客户可以观察到地址的变化的。重定向行为是浏览器做了至少两次的访问请求的。

(2)请求转发是一种服务器行为,通过HttpServletRequest对象获取RequestDispatcher来实现。

request.getRequsetDispatcher().forward(requset,response);

RequestDispatcher.forward方法的调用者与被调用者之间共享相同的request对象和response对象,它们属于同一个访问请求和响应过程;两次访问前后只是一次请求,转发后请求对象会保存,request中的数据会保留,地址栏的URL地址不会改变。(服务器内部转发,所有客户端看不到地址栏的改变)。RequestDispatcher.forward方法在服务器端内部将请求转发给另外一个资源,浏览器只知道发出了请求并得到了响应结果,并不知道在服务器程序内部发生了转发行为。
请求转发的过程:
客户浏览器发送http请求—-》web服务器接受此请求–》调用内部的一个方法在容器内部完成请求处理和转发动作—-》将目标资源发送给客户;在这里,转发的路径必须是同一个web容器下的url,其不能转向到其他的web路径上去,中间传递的是自己的容器内的request。在客户浏览器路径栏显示的仍然是其第一次访问的路径,也就是说客户是感觉不到服务器做了转发的。转发行为是浏览器只做了一次访问请求。


3. 实现原理

下面我们一起来看一下请求转发和请求重定向的实现原理。我们可以发现HttpServletRequest和HttpServletResponse是两个接口,而且我们在javaee的文档中并不能找到他们的实现类,那么他们到底是怎么实现的呢。
趋势,request和response只是规范中的一个名称而已。不是SUN提供的,这是由各个不同的Servlet提供商编写的,SUN只是规定这个类要实现HttpServletRequest和HttpServletResponse接口,并且规定了各个方法的用途,但具体是什么类是由各个提供商自己决定的。下面我们就来看一下HttpServletResponse.sendRedirect和RequestDispatcher.forward这两个方法的实现。
例如我使用的是tomcat服务器,那么这两个接口的实现就是由tomcat来完成的。
那么我们如何找到他们的实现类呢。我们可以使用下面的代码来直接输出实现类的名称。

System.out.println(response);
System.out.println(request.getRequestDispatcher("/login.jsp"));

输出结果如下:

org.apache.catalina.connector.ResponseFacade@1747c
org.apache.catalina.core.ApplicationDispatcher@1143c2b6

通过查找tomcat的源码我们可以进一步找到ResponseFacade和ApplicationDispatcher这两个类的源码。

首先分析一下ResponseFacade


/**
* Facade class that wraps a Coyote response object.
* All methods are delegated to the wrapped response.
*
* @author Remy Maucherat
* @author Jean-Francois Arcand
* @version $Id: ResponseFacade.java 939336 2010-04-29 15:00:41Z kkolinko
*/

文档注释指出,ResponseFacade是Response的一个外观类而已,方法的实现由Response实现。我们进一步看一下Response的实现,我们可以找到sendRedirect方法。


    /**
     * Send a temporary redirect to the specified redirect location URL.
     *
     * @param location Location URL to redirect to
     *
     * @exception IllegalStateException if this response has
     *  already been committed
     * @exception IOException if an input/output error occurs
     */
    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);

    }

我们可以大致推测出这个方法是通过重新设置http协议的location来实现重定向的,至于底层如何实现的,有兴趣可以继续深入。
下面我们来看一下,ApplicationDispatcher的forward方法。

     * Forward this request and response to another resource for processing.
     * Any runtime exception, IOException, or ServletException thrown by the
     * called servlet will be propogated to the caller.
     *
     * @param request The servlet request to be forwarded
     * @param response The servlet response to be forwarded
     *
     * @exception IOException if an input/output error occurs
     * @exception ServletException if a servlet exception occurs
     */
    public void forward(ServletRequest request, ServletResponse response)
        throws ServletException, IOException
    {
        if (Globals.IS_SECURITY_ENABLED) {
            try {
                PrivilegedForward dp = new PrivilegedForward(request,response);
                AccessController.doPrivileged(dp);
            } catch (PrivilegedActionException pe) {
                Exception e = pe.getException();
                if (e instanceof ServletException)
                    throw (ServletException) e;
                throw (IOException) e;
            }
        } else {
            doForward(request,response);
        }
    }

这个类内部定义了一个保护内部类,PrivilegedForward,用来实现请request和response对象的保持和转发。

<script type="text/javascript"> $(function () { $('pre.prettyprint code').each(function () { var lines = $(this).text().split('\n').length; var $numbering = $('<ul/>').addClass('pre-numbering').hide(); $(this).addClass('has-numbering').parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($('<li/>').text(i)); }; $numbering.fadeIn(1700); }); }); </script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值