RequestDispatcher

1、RequestDispatcher

RequestDispatcher實例對象是由Servlet引擎建立的,它用於包裝一個要被其餘資源調用的資源,例如Servlet、HTML文件,JSP文件等,並能夠經過其中的方法將客戶端的請求轉發給所包裝的資源。RequestDispatcher接口定義了forward和include方法,forward用於將請求轉發到RequestDispatcher對象封裝的資源,include用於將RequestDispatcher對象封裝的資源做爲當前響應內容的一部門包含進來。html

ServletContext接口中定義了兩個用於獲取RequestDispatcher對象的方法:java

  • getRequestDispatcher方法:返回包裝了某個路徑所指定的資源的RequestDispatcher對象,傳遞給該方法的路徑字符串必須以「/」開頭,「/」表明當前Web應用程序的根目錄(虛擬目錄)。WEB-INF目錄中的內容對RequestDispatcher對象是可見的,因此,傳遞給getRequestDispatcher方法的資源能夠是WEB-INF目錄中不能被外界訪問的文件。瀏覽器

  • getNamedDispatcher方法:返回包裝了某個Servlet或JSP文件的RequestDispatcher對象,傳遞給該方法的參數是在Web應用程序部署描述符中爲Servlet或JSP文件指定的友好名稱。架構

在ServletRequest接口中也定義了一個getRequestDispatcher方法,它與ServletContext.getRequestDispatcher的區別是:傳遞給這個方法的參數除了能夠採用「/」開頭的路徑字符串,還能夠採用非「/」開頭的相對路徑。app


2、用include方法實現資源包含

一、include方法

該方法用於將RequestDispatcher對象封裝的資源做爲當前響應內容的一部門包含進來,被包含的Servlet程序不能改變響應消息的狀態碼和響應頭。注意一點是:URL路徑一直是調用者程序的路徑。ide

二、應用細節

(1)被包含的資源是Servlet程序測試

IncludingServlet.java字體

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * 這是包含其餘資源的Servlet
 * @author  Lzj
 *
 */
public class IncludingServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html; charset=GB2312");
        PrintWriter out = response.getWriter();
        String china = "中國";
        // (1)必需要以/開頭,省略則報錯
        RequestDispatcher rd = getServletContext().getRequestDispatcher
            ("/servlet/IncludedServlet?p1=" + china);
            
        out.println("before including" + "<br>");
        rd.include(request, response);
        out.println("after including" + "<br>");
    }
}

IncludedServlet.java編碼

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * 這是被包含的Servlet
 * @author  Lzj
 *
 */
public class IncludedServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 被包含的Servlet程序不能改變響應消息的狀態碼和響應頭,下面兩條語句不起做用
        response.setContentType("text/html;charset=GB2312");
        response.setCharacterEncoding("GB2312");
        
        PrintWriter out = response.getWriter();
        out.println("中國" + "<br>");
        out.println("URI:" + request.getRequestURI() + "<br>");
        out.println("QueryString:" + request.getQueryString() + "<br>"); // 請求消息請求行的資源路徑並無參數,因此爲空
        out.println("parameter_p1:" + request.getParameter("p1") + "<br>");
        //out.println("id:" + request.getParameter("id") + "<br>");
    }
}

訪問包含其餘資源的Servlet,http://localhost:8888/requestDispatcherInclude/servlet/IncludingServletspa

  • 雖然調用者在調用IncludedServlet時向它傳遞了一個參數,可是轉發的請求消息並無變,因此QueryString爲空。

  • 亂碼是由於IncludingServlet程序的文本編碼是GBK,getParameter默認按ISO8859-1編碼獲取數據,又由於p1不是瀏覽器提交的參數,因此經過request.setCharacterEncoding("GB2312");可解決亂碼問題。

把IncludedServlet程序中最後一條語句註釋去掉,從新訪問http://localhost:8888/requestDispatcherInclude/servlet/IncludingServlet?id=1,注意添加了id參數

(2)被包含的資源是HTML文件

A、IncludingServlet中的response對象並未調用getWriter方法

IncludingServlet.java

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * 這是包含其餘資源的Servlet
 * @author Lzj
 *
 */
public class IncludingServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //response.setContentType("text/html; charset=GB2312");
        RequestDispatcher rd = getServletContext().getRequestDispatcher
            ("/test.html");
        rd.include(request, response);
    }
}

test.html

<!DOCTYPE html>
<html>
<head>
<title>Insert title here</title>
</head>
<body>
<h1>中文測試頁面</h1>
</body>
</html>

訪問包含其餘資源的Servlet,http://localhost:8888/requestDispatcherInclude/servlet/IncludingServlet

  • 凡是在Web.xml文件中找不到匹配<servlet-mapping>元素的URL訪問請求,也就是全部其餘Servlet都不處理的訪問請求,都將交給Tomcat中設置的一個缺省Servlet處理,客戶端對靜態HTML文件和圖片的訪問其實都是由缺省的Servlet來完成響應的,因此,調用靜態HTML文件與調用一個Servlet程序本質上沒有什麼區別。

  • Tomcat的缺省Servlet首先檢查當前HttpServletResponse對象是否已經調用了getWriter方法返回過PrintWriter對象,若是已經調用則使用getWriter方法返回的PrintWriter對象來輸出靜態HTML文件中的內容,不然,它將調用getOutputStream方法返回的ServletOutputStream對象將靜態HTML文件中的內容按字節流原封不動地寫入到客戶端。

  • 由於test.hmtl文本編碼與瀏覽器默認編碼GBK(可在「設置_高級設置_自定義字體」查看)相同(但瀏覽器默認編碼即使不是GBK,也可在Servlet程序調用setContentType方法來告訴瀏覽器用GBK顯示),因此訪問不會出現亂碼。

B、IncludingServlet中的response對象調用getWriter方法

IncludingServlet.java

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * 這是包含其餘資源的Servlet
 * @author Lzj
 *
 */
public class IncludingServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        PrintWriter out = response.getWriter();
        RequestDispatcher rd = getServletContext().getRequestDispatcher
            ("/test.html");
        rd.include(request, response);
    }
}

訪問包含其餘資源的Servlet,http://localhost:8888/requestDispatcherInclude/servlet/IncludingServlet

  • 由於調用者IncludingServlet已經調用過當前response對象的getWriter方法,因此Tomcat缺省Servlet將使用getWriter方法返回PrintWriter對象來輸出靜態HTML文件中的內容。但因爲IncludingServlet在調用getWriter方法以前,沒有經過任何方式指定它所得到的PrintWriter對象所採用的字符集編碼,該PrintWriter對象將test.html文件中的內容按默認的ISO8859-1編碼輸出到客戶端,由於ISO8859-1編碼不支持中文,因此出現亂碼。

  • 另外還需注意,若是把PrintWriter out = response.getWriter();語句挪到rd.include(request, response);語句後面,訪問時會報錯,由於在引入test.html文件以前response沒調用getWriter方法,Tomcat缺省Servlet則調用getOutputStream方法。兩個方法互相排斥,會報錯。


3、用forward方法實現請求轉發

一、forward方法

該方法用於將請求轉發到RequestDispatcher對象封裝的資源,Servlet程序在調用這個方法進行轉發以前能夠對請求進行一些前期的預處理。在MVC架構中,forward方法是一個核心方法,控制器組件(Controller)就是使用該方法來跳轉到視圖組件(Viewer),讓視圖組件產生響應內容返回給瀏覽器顯示。

二、應用細節

A、

ForwardTestServlet.java(註釋一、二、三、4爲注意事項)

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ForwardTestServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        // 一、在調用者程序和被調用者中設置響應狀態碼和響應頭都不會被忽略
        response.setContentType("text/hmtl; GB2312");
        PrintWriter out = response.getWriter();
    
        // 二、不顯示,由於調用forward以前寫入到緩衝區,緩衝區內容被清空
        out.println("before forward!"); 
    
        // 四、用於將緩衝區的內容強制刷新到客戶端,但後面語句將不起做用
        //response.flushBuffer();
    
        RequestDispatcher rd = getServletContext().getRequestDispatcher("/test.html");
        rd.forward(request, response);

        // 三、不顯示,由於調用forward以後寫入到緩衝區,寫入操做被忽略
        out.println("after forward!");
    }
}

訪問包含資源的Servlet,http://localhost:8888/requestDispatcherInclude/servlet/IncludingServlet

將response.flushBuffer()的註釋去掉,再次訪問,能夠發如今response.flushBuffer語句以前,輸出的內容能夠顯示

B、

test.html(注意連接有id參數,且值爲中文)

<!DOCTYPE html>
<html>
<head>
<title>Insert title here</title>
</head>
<body>
<a href="servlet/ForwardingServlet?id=中國">訪問包含其餘資源的Servlet</a>
</body>
</html>

ForwardingServlet.java

import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ForwardingServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String china = "中國";
        // 路徑必須以"/"開頭,表示相對於Web應用的根目錄(虛擬目錄)
        RequestDispatcher rd = getServletContext().getRequestDispatcher
            ("/servlet/ForwardedServlet?p1=" + china);
        rd.forward(request, response);
    }
}

ForwardedServlet.java

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ForwardedServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html; charset=GB2312");
        PrintWriter out = response.getWriter();
        
        request.setCharacterEncoding("GB2312");
        out.println("URI:" + request.getRequestURI() + "<br>");
        out.println("QueryString:" + request.getQueryString() + "<br>");
        out.println("URL:" + request.getRequestURL() + "<br>");
        out.println("id:" + request.getParameter("id") + "<br>");
        out.println("p1:" + request.getParameter("p1"));
    }
}

輸入http://localhost:8888/requestDispatcherForward/test.html,訪問test.html

點擊超連接

  • 與include方法對比,不難發現URI變爲被調用者的URI,即Servlet容器將根據目標資源路徑對當前request對象中的請求路徑和參數信息進行調整,因此QueryString不爲空

  • test.html的id參數是經過Get方式提交的參數,所以調用request.setCharacterEncoding方法並不能解決亂碼問題。能夠經過下面代碼解決

String id = new String(request.getParameter("id").getBytes("ISO8859-1"), "GB2312");
out.println("id:" + id + "<br>");

  • 由於p1不是瀏覽器提交的參數,而ForwardingServlet程序文本編碼是GBK,因此調用request.setCharacterEncoding("GB2312");依然可解決亂碼問題。

三、forward方法的相對路徑問題

使用<base>標籤指定基準地址

如:

String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getRequestURI();
out.println("<base href=\"" + basePath + "\">");
相關文章
相關標籤/搜索