請求重定向和請求轉發

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對象的保持和轉發。                                         

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章