官术网_书友最值得收藏!

理論篇

第1章 Servlet基礎(chǔ)

本章目標(biāo)

■ 了解動(dòng)態(tài)網(wǎng)站開發(fā)的相關(guān)技術(shù)

■ 理解Servlet的運(yùn)行原理及生命周期

■ 掌握Servlet的編寫及部署

■ 掌握Servlet對(duì)表單數(shù)據(jù)的處理

■ 掌握Servlet對(duì)HTTP請(qǐng)求報(bào)頭的處理

學(xué)習(xí)導(dǎo)航

任務(wù)描述

【描述1.D.1】

使用Servlet輸出“Hello World”頁面。

【描述1.D.2】

使用Servlet處理表單數(shù)據(jù),當(dāng)用戶提交的數(shù)據(jù)正確時(shí)(用戶名haier,密碼soft),輸出“登錄成功!”,否則提示“登錄失敗!”。

【描述1.D.3】

使用request對(duì)象讀取報(bào)頭信息,并打印在頁面中。

【描述1.D.4】

通過設(shè)置響應(yīng)報(bào)頭,實(shí)現(xiàn)動(dòng)態(tài)時(shí)鐘。

【描述1.D.5】

使用請(qǐng)求重定向和轉(zhuǎn)發(fā)兩種方式,使用戶自動(dòng)訪問重定向后的頁面,區(qū)分轉(zhuǎn)發(fā)和重定向的區(qū)別。

1.1 動(dòng)態(tài)網(wǎng)站技術(shù)概述

在《Web編程基礎(chǔ)》課程中已經(jīng)學(xué)習(xí)使用HTML、CSS、JavaScript等相關(guān)技術(shù)來建設(shè)靜態(tài)網(wǎng)站,其中,靜態(tài)網(wǎng)頁文件的擴(kuò)展名為“.htm”或“.html”,這些頁面不能與服務(wù)器進(jìn)行數(shù)據(jù)交互。隨著站點(diǎn)內(nèi)容和功能需求的不斷復(fù)雜化,單一的靜態(tài)網(wǎng)站技術(shù)往往不能滿足應(yīng)用的要求。例如,靜態(tài)網(wǎng)站無法完成將數(shù)據(jù)傳輸?shù)椒?wù)器上進(jìn)行處理并存儲(chǔ)到數(shù)據(jù)庫中,此時(shí)就需要?jiǎng)討B(tài)網(wǎng)站技術(shù)。

1.1.1 動(dòng)態(tài)網(wǎng)站技術(shù)

動(dòng)態(tài)網(wǎng)站并不是指具有動(dòng)畫功能的網(wǎng)站,而是指基于數(shù)據(jù)庫架構(gòu)的網(wǎng)站,一般由大量的動(dòng)態(tài)網(wǎng)頁(如JSP)、后臺(tái)處理程序(如Servlet)和用于存儲(chǔ)內(nèi)容的數(shù)據(jù)庫組成。所謂“動(dòng)態(tài)網(wǎng)頁”,本質(zhì)上與網(wǎng)頁上的各種動(dòng)畫、滾動(dòng)字幕等視覺上的“動(dòng)態(tài)效果”無關(guān)。動(dòng)態(tài)網(wǎng)頁可以是純文字內(nèi)容,也可以包含各種動(dòng)畫內(nèi)容,這些只是網(wǎng)頁具體內(nèi)容的表現(xiàn)形式。無論網(wǎng)頁是否具有動(dòng)態(tài)效果,采用動(dòng)態(tài)網(wǎng)站技術(shù)生成的網(wǎng)頁都稱為動(dòng)態(tài)網(wǎng)頁。

動(dòng)態(tài)網(wǎng)站一般具有以下幾個(gè)特點(diǎn)。

交互性:網(wǎng)頁會(huì)根據(jù)用戶的要求和選擇而動(dòng)態(tài)改變和響應(yīng)。例如,用戶在網(wǎng)頁中填寫表單信息并提交,服務(wù)器可以對(duì)數(shù)據(jù)進(jìn)行處理并保存到數(shù)據(jù)庫中,然后跳轉(zhuǎn)到相應(yīng)頁面。因此,動(dòng)態(tài)網(wǎng)站可以實(shí)現(xiàn)用戶注冊(cè)、信息發(fā)布、訂單管理等功能。

自動(dòng)更新:無須手動(dòng)更新HTML文檔,便會(huì)自動(dòng)生成新的頁面,大大減少了工作量。例如,在論壇中發(fā)布信息時(shí),后臺(tái)服務(wù)器可以產(chǎn)生新的網(wǎng)頁。

隨機(jī)性:在不同的時(shí)間、不同的用戶訪問同一網(wǎng)頁時(shí)可能產(chǎn)生不同的頁面。

注意 動(dòng)態(tài)網(wǎng)站一般采用動(dòng)靜結(jié)合的原則:網(wǎng)站中內(nèi)容頻繁更新的,可采用動(dòng)態(tài)網(wǎng)頁技術(shù);網(wǎng)站中內(nèi)容不需要更新的,則可采用靜態(tài)網(wǎng)頁進(jìn)行顯示。通常一個(gè)網(wǎng)站既包含動(dòng)態(tài)網(wǎng)頁也包含靜態(tài)網(wǎng)頁。

動(dòng)態(tài)網(wǎng)站技術(shù)有早期的CGI技術(shù),全名Common Gateway Interface(公用網(wǎng)關(guān)接口)。CGI提供了一種機(jī)制,可以實(shí)現(xiàn)客戶和服務(wù)器之間真正的雙向交互。這種技術(shù)為諸如在線用戶支持和電子商務(wù)等新思想的實(shí)現(xiàn)鋪平了道路,同時(shí)CGI技術(shù)因可以使用不同的語言編寫適合的CGI程序,如Visual Basic、Delphi和C/C++等,并且功能強(qiáng)大,被早期的很多網(wǎng)站采用。但由于編程困難、效率低下、修改復(fù)雜,所以逐漸被新技術(shù)所取代。目前被廣泛應(yīng)用的動(dòng)態(tài)網(wǎng)站技術(shù)主要有以下三種。

PHP(Hypertext Preprocessor):是超文本預(yù)處理器,其語法大量借鑒了C、Java、Perl等語言,只需要很少的編程知識(shí)就能使用PHP建立一個(gè)真正交互的Web站點(diǎn)。由于PHP開放源代碼,并且是免費(fèi)的,所以非常流行,是當(dāng)今Internet上最為火熱的腳本語言之一。

ASP(Active Server Pages):是一種類似HTML、Script與CGI結(jié)合體的技術(shù),它沒有提供自己專門的編程語言,允許用戶使用許多已有的腳本語言編寫ASP應(yīng)用程序。但ASP技術(shù)局限于微軟的操作系統(tǒng)平臺(tái)之上,主要工作環(huán)境為微軟的IIS應(yīng)用程序結(jié)構(gòu),而且ASP技術(shù)不能很容易地實(shí)現(xiàn)在跨平臺(tái)Web服務(wù)器上工作,因此一般只適合一些中小型站點(diǎn)。但目前由ASP升級(jí)演變而來的ASP.NET支持大型網(wǎng)站的開發(fā)。

JSP(Java Server Pages):是基于Java Servlet以及整個(gè)Java體系的Web開發(fā)技術(shù)。JSP是由SUN公司于1999年6月推出的新技術(shù),它與ASP有一定的相似之處,但JSP能在大部分的服務(wù)器上運(yùn)行,而且其應(yīng)用程序易于維護(hù)和管理,安全性能方面也被認(rèn)為是這三種基本動(dòng)態(tài)網(wǎng)站技術(shù)中最好的。

1.1.2 B/S架構(gòu)

在動(dòng)態(tài)網(wǎng)站技術(shù)中,一般使用瀏覽器作為客戶端,當(dāng)客戶在瀏覽器中發(fā)出請(qǐng)求時(shí),Web服務(wù)器得到請(qǐng)求后查找資源,然后向客戶返回一個(gè)結(jié)果,這就是B/S(Browser/Server)架構(gòu),如圖1-1所示。

圖1-1 B/S模型

在B/S架構(gòu)中,用戶的請(qǐng)求與Web服務(wù)器響應(yīng)需要通過Internet網(wǎng)絡(luò)從一臺(tái)計(jì)算機(jī)發(fā)送到另一臺(tái)計(jì)算機(jī),不同計(jì)算機(jī)之間是使用HTTP(HyperText Transfer Protocol)協(xié)議進(jìn)行通信的。HTTP是超文本傳輸協(xié)議,包含命令和傳輸信息,不僅用于Web訪問,也可以用于其他互聯(lián)網(wǎng)/內(nèi)聯(lián)網(wǎng)應(yīng)用系統(tǒng)之間的通信,從而實(shí)現(xiàn)各種資源信息的超媒體訪問集成。

1.2 Servlet簡介

Servlet是JavaEE架構(gòu)中的關(guān)鍵組成部分。JavaEE是基于分布式和多層結(jié)構(gòu)的企業(yè)級(jí)應(yīng)用開發(fā)規(guī)范和標(biāo)準(zhǔn)。目前,在企業(yè)應(yīng)用開發(fā)中不僅會(huì)使用傳統(tǒng)的JavaEE組件(例如JDBC、Servlet、EJB等),還會(huì)使用一些輕量級(jí)的框架結(jié)構(gòu)(例如Struts、Hibernate和Spring),以提高企業(yè)開發(fā)效率。在Java企業(yè)級(jí)開發(fā)應(yīng)用中會(huì)使用到的技術(shù)如圖1-2所示。Servlet技術(shù)是Sun公司提供的一種實(shí)現(xiàn)動(dòng)態(tài)網(wǎng)頁的解決方案,它是基于Java編程語言的Web服務(wù)器端編程技術(shù),主要用于在Web服務(wù)器端獲得客戶端的訪問請(qǐng)求信息并動(dòng)態(tài)生成對(duì)客戶端的響應(yīng)信息。此外,Servlet技術(shù)也是JSP技術(shù)的基礎(chǔ)。

圖1-2 JavaEE技術(shù)組成

Servlet是Web服務(wù)器端的Java應(yīng)用程序,它支持用戶交互式地瀏覽和修改數(shù)據(jù),生成動(dòng)態(tài)的Web頁面。比如,當(dāng)瀏覽器發(fā)送一個(gè)請(qǐng)求到服務(wù)器后,服務(wù)器會(huì)把請(qǐng)求送往一個(gè)特定的Servlet,這樣Servlet就能處理請(qǐng)求并構(gòu)造一個(gè)合適的響應(yīng)(通常以HTML網(wǎng)頁形式)返回給客戶,如圖1-3所示。

圖1-3 Servlet的作用

Servlet與普通Java程序相比,只是輸入信息的來源和輸出結(jié)果的目標(biāo)不一樣,例如,對(duì)于Java程序而言,用戶一般通過GUI窗口輸入信息,并在GUI窗口上獲得輸出結(jié)果,而對(duì)于Servlet程序而言,用戶一般通過瀏覽器輸入并獲取響應(yīng)結(jié)果。通常普通Java程序所能完成的大多數(shù)任務(wù),Servlet程序都可以完成。Servlet程序具有以下特點(diǎn):

高效

在傳統(tǒng)CGI中,如果有N個(gè)并發(fā)的對(duì)同一CGI程序的請(qǐng)求,則該CGI程序的代碼在內(nèi)存中重復(fù)裝載了N次;而對(duì)于Servlet,處理請(qǐng)求的是N個(gè)線程,只需要一份Servlet類代碼。在性能優(yōu)化方面,Servlet也比CGI有著更多的選擇,比如緩沖以前的計(jì)算結(jié)果、保持?jǐn)?shù)據(jù)庫連接的活動(dòng)等。

方便

Servlet提供了大量的實(shí)用工具例程,例如自動(dòng)地解析和解碼HTML表單數(shù)據(jù)、讀取和設(shè)置HTTP頭、處理Cookie、跟蹤會(huì)話狀態(tài)等。

功能強(qiáng)大

在Servlet中,許多使用傳統(tǒng)CGI程序很難完成的任務(wù)都可以輕松地完成。例如,Servlet能夠直接和Web服務(wù)器交互,而普通的CGI程序不能。Servlet還能夠在各個(gè)程序之間共享數(shù)據(jù),很容易地實(shí)現(xiàn)數(shù)據(jù)庫連接池之類的功能。

■ 良好的可移植性

Servlet是用Java語言編寫的,所以具備Java的可移植性特點(diǎn)。此外,Servlet API具有完善的標(biāo)準(zhǔn),支持Servlet規(guī)范的容器都可以運(yùn)行Servlet程序,例如Tomcat、Resin等。

1.3 第一個(gè)Servlet

編寫Servlet需要遵循其規(guī)范:

■ 創(chuàng)建Servlet時(shí),需要繼承HttpServlet類,同時(shí)需要導(dǎo)入Servlet API的兩個(gè)包:javax.servlet和javax.servlet.http。javax.servlet包提供了控制Servlet生命周期所必需的Servlet接口,是編寫Servlet時(shí)必須要實(shí)現(xiàn)的;javax.servlet.http包提供了從Servlet接口派生出的專門用于處理HTTP請(qǐng)求的抽象類和一般的工具類。

■ 根據(jù)數(shù)據(jù)的發(fā)送方式,覆蓋doGet()、doPost()方法之一或全部。doGet()和doPost()方法都有兩個(gè)參數(shù),分別為HttpServletRequest和HttpServletResponse類型。這兩個(gè)參數(shù)分別用于表示客戶端的請(qǐng)求和服務(wù)器端的響應(yīng)。通過HttpServletRequest,可以從客戶端獲得發(fā)送過來的信息;通過HttpServletResponse,可以讓服務(wù)器端對(duì)客戶端做出響應(yīng),最常用的就是向客戶端發(fā)送信息。關(guān)于這兩個(gè)參數(shù),將在后續(xù)內(nèi)容中詳細(xì)講解。

注意 如果在瀏覽器中直接輸入地址來訪問Servlet資源,屬于使用GET方式訪問。

下述代碼用于實(shí)現(xiàn)任務(wù)描述1.D.1,使用Servlet輸出“Hello World”頁面。

【描述1.D.1】HelloServlet.java

    // 創(chuàng)建一個(gè)Servlet類,繼承HttpServlet
    public class HelloServlet extends HttpServlet
    {
        // 重寫doGet()
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            // 設(shè)置響應(yīng)到客戶端的文本類型為HTML
            response.setContentType("text/html");
            // 獲取輸出流
            PrintWriter out = response.getWriter();
            out.println(" Hello World");
        }
    }

上述代碼會(huì)向客戶端瀏覽器中打印“Hello World”信息。通過response對(duì)象的getWriter()方法可以獲取向客戶端輸出信息的輸出流:

    PrintWriter out = response.getWriter();

調(diào)用輸出流的println()方法可以在客戶端瀏覽器中打印消息。例如:

    out.println(" Hello World");

下面在Web應(yīng)用的部署文件web.xml中注冊(cè)此Servlet信息。

【描述1.D.1】在web.xml中配置Servlet

    <servlet>
        <display-name>Hello</display-name>
        <!-- Servlet的引用名 -->
        <servlet-name>Hello</servlet-name>
        <!-- 所配置的Servlet類的完整類路徑 -->
        <servlet-class>com.haiersoft.ch01.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <!-- 前面配置的Servlet引用名-->
        <servlet-name>Hello</servlet-name>
        <!-- 訪問當(dāng)前Servlet的URL -->
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

在上述配置信息中,需要注意以下幾個(gè)方面:

■ Servlet別名,即<servlet-name>和</servlet-name>之間的命名可以隨意命名,但要遵循命名規(guī)范。

■ <servlet>和<servlet-mapping>元素可以配對(duì)出現(xiàn),通過Servlet別名進(jìn)行匹配。<servlet>元素也可以單獨(dú)出現(xiàn),通常用于初始化操作。

■ URL引用,即<url-pattern>和</url-pattern>之間的命名通常以“/”開頭。

啟動(dòng)Tomcat,在IE中訪問http://localhost:8080/ch01/hello,運(yùn)行結(jié)果如圖1-4所示。

圖1-4 運(yùn)行結(jié)果

注意 關(guān)于Web應(yīng)用的開發(fā)過程及配置,可參考實(shí)踐1.G.2指導(dǎo)部分。

1.4 Servlet的生命周期

Servlet是運(yùn)行在服務(wù)器上的,其生命周期由Servlet容器負(fù)責(zé)。Servlet生命周期是指Servlet實(shí)例從創(chuàng)建到響應(yīng)客戶請(qǐng)求直至銷毀的過程。Servlet API中定義了關(guān)于Servlet生命周期的3個(gè)方法。

init():用于Servlet初始化。當(dāng)容器創(chuàng)建Servlet實(shí)例后,會(huì)自動(dòng)調(diào)用此方法。

service():用于服務(wù)處理。當(dāng)客戶端發(fā)出請(qǐng)求,容器會(huì)自動(dòng)調(diào)用此方法進(jìn)行處理,并將處理結(jié)果響應(yīng)到客戶端。service()方法有兩個(gè)參數(shù),分別接收ServletRequest接口和ServletResponse接口的對(duì)象來處理請(qǐng)求和響應(yīng)。

destroy():用于銷毀Servlet。當(dāng)容器銷毀Servlet實(shí)例時(shí)自動(dòng)調(diào)用此方法,釋放Servlet實(shí)例,清除當(dāng)前Servlet所持有的資源。

Servlet生命周期概括為以下幾個(gè)階段。

01 裝載Servlet:這項(xiàng)操作一般是動(dòng)態(tài)執(zhí)行的,有些服務(wù)器提供了相應(yīng)的管理功能,可以在啟動(dòng)的時(shí)候就裝載Servlet。

02 創(chuàng)建一個(gè)Servlet實(shí)例:容器創(chuàng)建Servlet的一個(gè)實(shí)例對(duì)象。

03 初始化:容器調(diào)用init()方法對(duì)Servlet實(shí)例進(jìn)行初始化。

04 服務(wù):當(dāng)容器接收到對(duì)此Servlet的請(qǐng)求時(shí),將調(diào)用service()方法響應(yīng)客戶的請(qǐng)求。

05 銷毀:容器調(diào)用destroy()方法銷毀Servlet實(shí)例。

在Servlet生命周期的這幾個(gè)階段中,初始化init()方法僅執(zhí)行一次,是在服務(wù)器裝載Servlet時(shí)執(zhí)行的,以后無論有多少客戶訪問此Servlet,都不會(huì)重復(fù)執(zhí)行init()。即此Servlet在Servlet容器中只有單一實(shí)例;當(dāng)多個(gè)用戶訪問此Servlet時(shí),會(huì)分為多個(gè)線程訪問此Servlet實(shí)例對(duì)象的service()方法。在service()方法內(nèi),容器會(huì)對(duì)客戶端的請(qǐng)求方式進(jìn)行判斷,如果是Get方式提交,則調(diào)用doGet()進(jìn)行處理;如果是Post方式提交,則調(diào)用doPost()進(jìn)行處理。圖1-5說明了Servlet生命周期的不同階段。

下面代碼演示了Servlet的生命周期。

圖1-5 Servlet的生命周期

【代碼1-1】ServletLife.java

    public class ServletLife extends HttpServlet {
        /**
        * 構(gòu)造方法
        */
        public ServletLife() {
            super();
        }
        /**
        * 初始化方法
        */
        public void init(ServletConfig config) throws ServletException {
            System.out.println("初始化時(shí),init()方法被調(diào)用!");
        }
        protected void doGet(HttpServletRequest request,
                HttpServletResponse response) throws ServletException, IOException {
            System.out.println("處理請(qǐng)求時(shí),doGet()方法被調(diào)用。");
        }
        protected void doPost(HttpServletRequest request,
                HttpServletResponse response) throws ServletException, IOException {
            System.out.println("處理請(qǐng)求時(shí),doPost()方法被調(diào)用。");
        }
        /**
        * 用于釋放資源
        */
        public void destroy() {
            super.destroy();
            System.out.println("釋放系統(tǒng)資源時(shí),destroy()方法被調(diào)用!");
        }
    }

啟動(dòng)Tomcat,在IE中訪問http://localhost:8080/ch01/ServletLife,觀察控制臺(tái)輸出信息,如圖1-6所示。

圖1-6 Servlet生命周期

打開多個(gè)IE窗口,訪問此Servlet,觀察控制臺(tái)輸出,會(huì)發(fā)現(xiàn)init()方法只運(yùn)行一次,而service()方法會(huì)對(duì)每次請(qǐng)求都做出響應(yīng)。

1.5 Servlet數(shù)據(jù)處理

Servlet數(shù)據(jù)處理主要包括讀取表單數(shù)據(jù)、HTTP請(qǐng)求報(bào)頭的處理和HTTP響應(yīng)報(bào)頭的設(shè)置。

1.5.1 讀取表單數(shù)據(jù)

當(dāng)訪問Internet網(wǎng)站時(shí),在瀏覽器地址欄中會(huì)經(jīng)常看到如下所述的字符串:

    http://host/path?usr=tom&dest=ok

該字符串問號(hào)后面的部分為表單數(shù)據(jù)(Form Data)或查詢數(shù)據(jù)(Query Data),這些數(shù)據(jù)以“name=value”形式通過URL傳送,多個(gè)數(shù)據(jù)使用“&”分開,這種形式也稱為“查詢字符串”。查詢字符串緊跟在URL中的“?”后面,所有“名/值”對(duì)會(huì)被傳遞到服務(wù)器,這是服務(wù)器獲取客戶端信息所采用的最常見的方式。

表單數(shù)據(jù)可以通過GET請(qǐng)求方式提交給服務(wù)器,此種方式將數(shù)據(jù)跟在問號(hào)后附加到URL的結(jié)尾(查詢字符串形式);也可以采用POST請(qǐng)求方式提交給服務(wù)器,此種方式將在地址欄看不到表單數(shù)據(jù)信息,可用于大量數(shù)據(jù)的傳輸,并且比GET方式更安全。

在學(xué)習(xí)處理Form表單數(shù)據(jù)前,先來回顧《Web編程基礎(chǔ)》中學(xué)過的關(guān)于表單的基本知識(shí)。

(1)使用Form標(biāo)簽創(chuàng)建HTML表單。

使用action屬性指定對(duì)表單進(jìn)行處理的Servlet或JSP頁面的地址,可以使用絕對(duì)或相對(duì)URL。例如:

    <form action="...">...</form>

如果省略action屬性,那么數(shù)據(jù)將提交給當(dāng)前頁面對(duì)應(yīng)的URL。

(2)使用輸入元素收集用戶數(shù)據(jù)。

將這些元素放在Form標(biāo)簽內(nèi),并為每個(gè)輸入元素賦予一個(gè)name。文本字段是最常用的輸入元素,其創(chuàng)建方式如下:

    <input type="text" name="...">

(3)在接近表單的尾部放置提交按鈕。

例如:

    <input type="submit"/>

單擊提交按鈕時(shí),瀏覽器會(huì)將數(shù)據(jù)提交給表單action對(duì)應(yīng)的服務(wù)器端程序。

1. Form表單數(shù)據(jù)

通過HttpServletRequest對(duì)象可以讀取Form標(biāo)簽中的表單數(shù)據(jù)。HttpServletRequest接口在javax.servlet.http包中定義,它擴(kuò)展了ServletRequest,并定義了描述一個(gè)HTTP請(qǐng)求的方法。當(dāng)客戶端請(qǐng)求Servlet時(shí),一個(gè)HttpServletRequest類型的對(duì)象會(huì)被傳遞到Servlet的service()方法,進(jìn)而傳遞到doGet()或doPost()方法中去。此對(duì)象中封裝了客戶端的請(qǐng)求擴(kuò)展信息,包括HTTP方法(即GET或POST)、Cookie、身份驗(yàn)證和表單數(shù)據(jù)等信息。

表1-1列出了HttpServletRequest接口中用于讀取表單數(shù)據(jù)的方法。

表1-1 HttpServletRequest接口中讀取表單數(shù)據(jù)的方法

默認(rèn)情況下,request.getParameter()使用服務(wù)器的當(dāng)前字符集解釋輸入。要改變這種默認(rèn)行為,需要使用setCharacterEncoding(String env)方法來設(shè)置字符集,例如:

    request.setCharacterEncoding("GBK");

下述內(nèi)容用于實(shí)現(xiàn)任務(wù)描述1.D.2,使用Servlet處理表單數(shù)據(jù),當(dāng)用戶提交的數(shù)據(jù)正確時(shí)(用戶名haier,密碼soft),輸出“登錄成功!”,否則提示“登錄失敗!”。

(1)首先編寫靜態(tài)頁面,用于接收用戶信息。

【描述1.D.2】index.html

    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=gbk">
    <title>登錄</title>
    <script language="javascript" type="">
            function LoginSubmit(){
                var user=document.Login.loginName.value;
                var pass=document.Login.password.value;
                if(user==null||user==""){
                    alert("請(qǐng)?zhí)顚懹脩裘?);
                }
                else if(pass==null||pass==""){
                    alert("請(qǐng)?zhí)顚懨艽a");
                }
                else document.Login.submit();
            }
    </script>
    </head>
    <body>
    <form method="POST" name="Login" action="LoginServlet">
      <p align="left">
      用戶名:<input type="text" name="loginName" size="20"></p>
      <p align="left">
      密&nbsp; 碼:<input type="password" name="password" size="20"></p>
      <p align="left">
      <input type="button" value="提交" name="B1" onclick="LoginSubmit()">
      <input type="reset" value="重置" name="B2"></p>
    </form>
    </body>
    </html>

上述HTML代碼中,使用JavaScript對(duì)用戶表單進(jìn)行初始驗(yàn)證,驗(yàn)證成功后才提交給LoginSevlet進(jìn)行處理。

(2)編寫Servlet處理用戶提交表單數(shù)據(jù)。

【描述1.D.2】LoginServlet.java

    public class LoginServlet extends HttpServlet {
        public LoginServlet() {
            super();
        }
    public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            doPost(request, response);
        }
    public void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            // 設(shè)置請(qǐng)求的編碼字符為GBK(中文編碼)
            request.setCharacterEncoding("GBK");
            // 設(shè)置響應(yīng)的文本類型為html,編碼字符為GBK
            response.setContentType("text/html;charset=GBK");
            // 獲取輸出流
            PrintWriter out = response.getWriter();
            // 獲取表單數(shù)據(jù)
            String pass = request.getParameter("password");
            String user = request.getParameter("loginName");
            if ("haier".equals(user) && "soft".equals(pass)) {
                out.println("登錄成功!");
            } else {
                out.println("登錄失敗!");
            }
    }
    }

上述代碼中,在doGet()方法中調(diào)用了doPost()方法,這樣不管用戶以什么方式提交,處理過程都一樣。因?yàn)轫撁嬷惺褂昧酥形模瑸榱朔乐钩霈F(xiàn)中文亂碼問題,所以需要設(shè)置請(qǐng)求和響應(yīng)的編碼字符集,使之能夠支持中文,如下所示:

    request.setCharacterEncoding("GBK");
    response.setContentType("text/html;charset=GBK");

獲取表單中的數(shù)據(jù)時(shí),使用getParameter()方法通過參數(shù)名獲得參數(shù)值,例如:

    String pass = request.getParameter("password");

上面語句通過參數(shù)名“password”來獲取該參數(shù)的值。

注意 如果index.html中表單的提交方式為GET方式,則在瀏覽器地址欄中會(huì)出現(xiàn)查詢字符串形式的表單數(shù)據(jù)(如password=soft&user=haier),但在LoginServlet中獲取參數(shù)值的方式完全相同。

(3)在web.xml中注冊(cè)該Servlet。

【描述1.D.2】web.xml

    <servlet>
        <display-name>LoginServlet</display-name>
        <servlet-name>LoginServlet</servlet-name>
        <servlet-class>com.haiersoft.ch01.LoginServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>LoginServlet</servlet-name>
        <url-pattern>/LoginServlet</url-pattern>
    </servlet-mapping>

上述代碼中,注冊(cè)了一個(gè)名為“LoginServlet”的Servlet,當(dāng)請(qǐng)求的相對(duì)URL為“/LoginServlet”時(shí),Servlet容器會(huì)將請(qǐng)求交給該Servlet進(jìn)行處理。

啟動(dòng)Tomcat,在IE中訪問http://localhost:8080/ch01/index.html,運(yùn)行結(jié)果如圖1-7所示。

圖1-7 index.html頁面

在“用戶名”文本欄中輸入“haier”,在“密碼”文本框中輸入“soft”。然后單擊“提交”按鈕,顯示結(jié)果如圖1-8所示。

當(dāng)輸入錯(cuò)誤的用戶名或密碼時(shí),則顯示“登錄失敗!”,如圖1-9所示。

圖1-8 LoginServlet驗(yàn)證成功

圖1-9 LoginServlet驗(yàn)證失敗

Form表單數(shù)據(jù)中除了普通的表單項(xiàng)之外,在實(shí)際開發(fā)中會(huì)廣泛應(yīng)用到隱藏域。隱藏域是隱藏的HTML表單變量,可以用來存儲(chǔ)狀態(tài)信息,操作起來與一般的HTML輸入域(比如文本輸入域、復(fù)選框和單選按鈕)類似,同樣會(huì)被提交到服務(wù)器。隱藏域與普通的HTML輸入域之間的不同之處就在于客戶端不能看到或修改隱藏域的值。

隱藏域可以用來在客戶端和服務(wù)器之間透明地傳輸狀態(tài)信息,示例代碼如下。

【代 碼1-2】hidden.html

    <html>
    <head>
    <title>隱藏域</title>
    </head>
    <body bgcolor="blue">
    <form method="post" action="nameservlet">
    <p> 請(qǐng)輸入用戶名:<br>
    <input type="text" name="uname"><br>
    <input type="hidden" name="bcolor" value="blue"><br>
    <input type="submit" value="submit">
    </form>
    </body>
    </html>

上述代碼中使用隱藏域?qū)⒂脩羲矚g的背景色傳遞給服務(wù)器。在服務(wù)器端獲取隱藏域的數(shù)據(jù)與表單其他元素一樣,都是使用getParameter()方法通過參數(shù)名獲取其數(shù)據(jù)值。

2. 查詢字符串

查詢字符串是表單數(shù)據(jù)的另一種情況,它們實(shí)質(zhì)上是相同的。同樣,服務(wù)器端的Servlet也是通過HttpServletRequest對(duì)象的getParameter()方法或者getParameterValues()方法讀取URL中查詢字符串的信息,然后根據(jù)信息可以進(jìn)行查詢,再把查詢的結(jié)果返回。

下述代碼演示查詢字符串的應(yīng)用。

【代碼1-3】querystr.html

    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=GBK">
    <title> 查詢字符串</title>
    </head>
    <body>
    <a href="TestURL?id=2010">下一頁</a>
    </body>
    </html>

上述代碼中,在超鏈接的URL中使用查詢字符串,在“?”后添加了“id=2010”,該語句傳遞了一個(gè)參數(shù)id,其值為2010。

【代碼1-4】TestURL

    public class TestURL extends HttpServlet {
        public TestURL() {
            super();
        }
        protected void doGet(HttpServletRequest request,
                HttpServletResponse response) throws ServletException, IOException {
            doPost(request, response);
        }
        protected void doPost(HttpServletRequest request,
                HttpServletResponse response) throws ServletException, IOException {
            response.setContentType("text/html;charset=GBK");
            PrintWriter out = response.getWriter();
            String id = request.getParameter("id");
            out.println("URL參數(shù)值是:" + id);
        }
    }

在上述代碼中,使用request對(duì)象的getParameter()方法獲取URL中的參數(shù)值,并輸出。

啟動(dòng)Tomcat,在IE中訪問http://localhost:8080/ch01/querystr.html,顯示結(jié)果如圖1-10所示。

單擊“下一頁”超鏈接,顯示結(jié)果如圖1-11所示。

圖1-10 index.html

圖1-11 TestURL結(jié)果

1.5.2 處理HTTP請(qǐng)求報(bào)頭

客戶端瀏覽器向服務(wù)器發(fā)送請(qǐng)求的時(shí)候,除了用戶輸入的表單數(shù)據(jù)或者查詢數(shù)據(jù)之外,通常還會(huì)在GET/POST請(qǐng)求行后面加上一些附加的信息;而在服務(wù)器向客戶端的請(qǐng)求做出響應(yīng)的時(shí)候,也會(huì)自動(dòng)向客戶端發(fā)送一些附加的信息。這些附加信息被稱為HTTP報(bào)頭,信息附加在請(qǐng)求信息后面稱為HTTP請(qǐng)求報(bào)頭,而附加在響應(yīng)信息后面則稱為HTTP響應(yīng)報(bào)頭。在Servlet中可以獲取或設(shè)置這些報(bào)頭的信息。

報(bào)頭信息的讀取比較簡單:只需將報(bào)頭的名稱作為參數(shù),調(diào)用HttpServletRequest的getHeader方法;如果當(dāng)前的請(qǐng)求中提供了對(duì)應(yīng)的報(bào)頭信息,則返回一個(gè)String,否則返回null。

另外,這些報(bào)頭的參數(shù)名稱不區(qū)分大小寫,也就是說,也可以通過getHeader("user-agent")來獲得User-Agent報(bào)頭。常用的HTTP請(qǐng)求報(bào)頭如表1-2所示。

表1-2 常用HTTP請(qǐng)求報(bào)頭

盡管getHeader()方法是讀取輸入報(bào)頭的通用方式,但由于幾種報(bào)頭的應(yīng)用很普遍,因此HttpServletRequest為它們提供了專門的訪問方法,如表1-3所示。

表1-3 HttpServletRequest獲取報(bào)頭信息的方法

下述代碼用于實(shí)現(xiàn)任務(wù)描述1.D.3,演示報(bào)頭信息的讀取方式。

【描述1.D.3】HttpHeadServlet.java

    public class HttpHeadServlet extends HttpServlet {
        protected void doGet(HttpServletRequest request,
                HttpServletResponse response) throws ServletException, IOException {
            doPost(request, response);
        }
        protected void doPost(HttpServletRequest request,
                HttpServletResponse response) throws ServletException, IOException {
            response.setContentType("text/html;charset=gbk");
            PrintWriter out = response.getWriter();
            StringBuffer buffer = new StringBuffer();
            buffer.append("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 "
                    + "Transitional//EN\">");
            buffer.append("<html>");
            buffer.append("<head><title>");
            String title = "請(qǐng)求表頭信息";
            buffer.append(title);
            buffer.append("</title></head>");
            buffer.append("<body>");
            buffer.append("<h1 align='center'>" + title + "</h1>");
            buffer.append("<b>Request Method: </b>");
            buffer.append(request.getMethod() + "<br/>");
            buffer.append("<b>Request URL: </b>");
            buffer.append(request.getRequestURI() + "<br/>");
            buffer.append("<b>Request Protocol: </b>");
            buffer.append(request.getProtocol() + "<br/>");
            buffer.append("<b>Request Local: </b>");
            buffer.append(request.getLocale() + "<br/><br/>");
            buffer.append("<table border='1' align='center'>");
            buffer.append("<tr bgcolor='#FFAD00'>");
            buffer.append("<th>Header Name</th><th>Header Value</th>");
            buffer.append("</tr>");
            Enumeration<String> headerNames = request.getHeaderNames();
            while (headerNames.hasMoreElements()) {
                String headerName = (String) headerNames.nextElement();
                buffer.append("<tr>");
                buffer.append("<td>" + headerName + "</td>");
                buffer.append("<td>" + request.getHeader(headerName) + "</td>");
                buffer.append("</tr>");
            }
            buffer.append("</body>");
            buffer.append("</html>");
            out.println(buffer.toString());
        }
    }

上述代碼中,通過調(diào)用request對(duì)象中的getMethod()方法來獲取用戶請(qǐng)求方式;調(diào)用getRequestURI()方法來獲取用戶請(qǐng)求路徑;調(diào)用getHeaderNames()方法返回所有請(qǐng)求報(bào)頭名稱的集合,遍歷此集合并使用getHeader()提取報(bào)頭信息顯示。

啟動(dòng)Tomcat,在IE中訪問http://localhost:8080/ch01/HttpHeadServlet,運(yùn)行結(jié)果如圖1-12所示。

圖1-12 請(qǐng)求報(bào)頭信息

1.5.3 設(shè)置HTTP響應(yīng)報(bào)頭

在Servlet中,可以通過HttpServletResponse的setHeader()方法來設(shè)置HTTP響應(yīng)報(bào)頭,它接收兩個(gè)參數(shù),用于指定響應(yīng)報(bào)頭的名稱和對(duì)應(yīng)的值,語法格式如下:

    setHeader(String headerName,String headerValue)

常用的HTTP響應(yīng)報(bào)頭如表1-4所示。

表1-4 常用的HTTP響應(yīng)報(bào)頭

注意 一些舊版本的瀏覽器只支持HTTP 1.0的報(bào)頭,所以,為了保證程序具有良好的兼容性,應(yīng)該慎重地使用這些報(bào)頭,或者使用HttpServletRequest的getRequestProtocol()方法獲得HTTP的版本后再做選擇。

除了setHeader()方法外,還有兩個(gè)方法用于設(shè)置日期或者整型數(shù)據(jù)格式報(bào)頭:

    setDateHeader(String headerName, long ms)

    setIntHeader(String headerName, int headerValue)

此外,對(duì)于一些常用的報(bào)頭,在API中也提供了更方便的方法來設(shè)置它們,如表1-5所示。

表1-5 HttpServletResponse響應(yīng)方法

下述代碼用于實(shí)現(xiàn)任務(wù)描述1.D.4,通過設(shè)置響應(yīng)報(bào)頭,實(shí)現(xiàn)動(dòng)態(tài)時(shí)鐘。

【描述1.D.4】DateServlet.java

    public class DateServlet extends HttpServlet {
        public void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException { // 獲得一個(gè)向客戶發(fā)送數(shù)據(jù)的輸出流
            response.setContentType("text/html; charset=GBK");// 設(shè)置響應(yīng)的MIME類型
            PrintWriter out = response.getWriter();
            out.println("<html>");
            out.println("<body>");
            response.setHeader("Refresh", "1"); // 設(shè)置Refresh的值
            out.println("現(xiàn)在時(shí)間是:");
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
            out.println("<br/>" + sdf.format(new Date()));
            out.println("</body>");
            out.println("</html>");
        }
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            doPost(request, response);
        }
    }

上述代碼中,通過設(shè)置響應(yīng)報(bào)頭,使得客戶端每隔一秒訪問一次當(dāng)前Servlet,從而在客戶端能夠動(dòng)態(tài)地觀察時(shí)鐘的變化。實(shí)現(xiàn)每隔一秒動(dòng)態(tài)刷新的功能代碼如下:

    response.setHeader("Refresh", "1");

其中,Refresh為響應(yīng)頭部信息;1是時(shí)間間隔值,以秒為單位。

啟動(dòng)Tomcat,在IE中訪問http://localhost:8080/ch01/DateServlet,運(yùn)行結(jié)果如圖1-13所示。

圖1-13 動(dòng)態(tài)時(shí)鐘

注意 在任務(wù)描述1.D.4中,通過不斷地刷新當(dāng)前頁面來訪問DateServlet,從而實(shí)現(xiàn)了動(dòng)態(tài)時(shí)鐘,在本書第8章通過AJAX技術(shù)可以實(shí)現(xiàn)無刷新時(shí)鐘,讀者可以通過觀察顯示效果對(duì)這兩種方式進(jìn)行比較。

1.6 重定向和請(qǐng)求轉(zhuǎn)發(fā)

重定向和請(qǐng)求轉(zhuǎn)發(fā)是Servlet處理完數(shù)據(jù)后進(jìn)行頁面跳轉(zhuǎn)的兩種主要方式。

1.6.1 重定向

重定向是指頁面重新定位到某個(gè)新地址,之前的Request失效,進(jìn)入一個(gè)新的Request,且跳轉(zhuǎn)后瀏覽器地址欄內(nèi)容將變?yōu)樾碌闹付ǖ刂贰V囟ㄏ蚴峭ㄟ^HttpServletResponse對(duì)象的sendRedirect()方法來實(shí)現(xiàn)的,該方法用于生成302響應(yīng)碼和Location響應(yīng)頭,從而通知客戶端去重新訪問Location響應(yīng)頭中指定的URL,其語法格式如下:

    pubilc void sendRedirect(java.lang.String location)throws java.io.IOException

其中:

■ location參數(shù)指定了重定向的URL,它可以是相對(duì)路徑也可以是絕對(duì)路徑。

使用sendRedirect()方法不僅可以重定向到當(dāng)前應(yīng)用程序中的其他資源,還可以重定向到其他應(yīng)用程序中的資源,例如:

    response.sendRedirect("/ch01/index.html");

上面語句重定向到當(dāng)前站點(diǎn)(ch01)的根目錄下的index.html界面。

下述代碼用于實(shí)現(xiàn)任務(wù)描述1.D.5,使用請(qǐng)求重定向方式使用戶自動(dòng)訪問重定向后的頁面。

【描述1.D.5】RedirectServlet.java

    public class RedirectServlet extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            response.setContentType("text/html; charset="GBK");
            PrintWriter out = response.getWriter();
            out.println("重定向前");
            response.sendRedirect(request.getContextPath() + "/myservlet");
            out.println("重定向后");
        }
    }

在web.xml配置文件中配置RedirectServlet的<url-pattern>為“/redirect”。

其中,myservlet對(duì)應(yīng)的Servlet代碼如下所示。

【描述1.D.5】MyServlet.java

    public class MyServlet extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            // 設(shè)置響應(yīng)到客戶端的文本類型為HTML
            response.setContentType("text/html; charset=GBK");
            // 獲取輸出流
            PrintWriter out = response.getWriter();
            out.println("重定向和請(qǐng)求轉(zhuǎn)發(fā)");
        }
    }

在web.xml配置文件中配置MyServlet的<url-pattern>為“/myservlet”。

啟動(dòng)Tomcat,在IE中訪問http://localhost:8080/ch01/redirect,顯示出了MyServlet輸出網(wǎng)頁中的內(nèi)容,這時(shí)瀏覽器地址欄中的地址變成了MyServlet的URL“http://localhost:8080/ch01/myservlet”,結(jié)果如圖1-14所示。

圖1-14 重定向地址欄變化

1.6.2 請(qǐng)求轉(zhuǎn)發(fā)

請(qǐng)求轉(zhuǎn)發(fā)是指將請(qǐng)求再轉(zhuǎn)發(fā)到另一頁面,此過程依然在Request范圍內(nèi),轉(zhuǎn)發(fā)后瀏覽器地址欄內(nèi)容不變。請(qǐng)求轉(zhuǎn)發(fā)使用RequestDispatcher接口中的forward()方法來實(shí)現(xiàn),該方法可以把請(qǐng)求轉(zhuǎn)發(fā)到另外一個(gè)資源,并讓該資源對(duì)瀏覽器的請(qǐng)求進(jìn)行響應(yīng)。

RequestDispatcher接口有以下兩個(gè)方法。

forward()方法:請(qǐng)求轉(zhuǎn)發(fā),可以從當(dāng)前Servlet跳轉(zhuǎn)到其他Servlet。

include()方法:引入其他Servlet。

RequestDispatcher是一個(gè)接口,通過使用HttpRequest對(duì)象的getRequestDispalcher()方法可以獲得該接口的實(shí)例對(duì)象,例如:

    RequestDispatcher rd = request.getRequestDispatcher(path);
    rd.forward(request,response);

下述代碼用于實(shí)現(xiàn)任務(wù)描述1.D.5,使用請(qǐng)求轉(zhuǎn)發(fā)方式使用戶自動(dòng)訪問請(qǐng)求轉(zhuǎn)發(fā)后的頁面。

【描述1.D.5】ForwardServlet.java

    //請(qǐng)求轉(zhuǎn)發(fā)
    public class ForwardServlet extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            response.setContentType("text/html; charset=GBK");
            PrintWriter out = response.getWriter();
            out.println("請(qǐng)求轉(zhuǎn)發(fā)前");
            RequestDispatcher rd = request.getRequestDispatcher("/myservlet");
            rd.forward(request, response);
            out.println("請(qǐng)求轉(zhuǎn)發(fā)后");
        }
    }

在IE中訪問http://localhost:8080/ch01/forward,瀏覽器中顯示出了MyServlet輸出網(wǎng)頁中的內(nèi)容,這時(shí)瀏覽器地址欄中的地址不會(huì)發(fā)生改變,結(jié)果如圖1-15所示。

圖1-15 請(qǐng)求轉(zhuǎn)發(fā)地址欄變化

通過上述ForwardServlet和RedirectServlet的運(yùn)行結(jié)果可以看出,轉(zhuǎn)發(fā)和重定向兩種方式在調(diào)用后地址欄中的URL是不同的,前者的地址欄不變,后者地址欄中的URL變成目標(biāo)URL。

此外,轉(zhuǎn)發(fā)和重定向最主要的區(qū)別是:轉(zhuǎn)發(fā)前后共享同一個(gè)request對(duì)象,而重定向前后不在一個(gè)請(qǐng)求中。

為了驗(yàn)證請(qǐng)求轉(zhuǎn)發(fā)和重定向的區(qū)別,在示例中會(huì)用到HttpServletRequest的存取和讀取屬性值的兩個(gè)方法。

getAttribute(String name):取得name的屬性值,如果屬性不存在則返回null。

setAttribute(String name,Object value):將value對(duì)象以name名稱綁定到request對(duì)象中。

注意 除HttpServletRequest接口外,HttpSession和ServletContext接口也擁有g(shù)etAttribute()和setAttribute()方法,分別用來讀取和設(shè)置這兩類對(duì)象中的屬性值。

下述內(nèi)容用于實(shí)現(xiàn)任務(wù)描述1.D.5,通過請(qǐng)求參數(shù)的傳遞來驗(yàn)證forward()方法和sendRedirect()方法在request對(duì)象共享上的區(qū)別。

(1)改寫RedirectServlet,在sendRedirect()方法中加上查詢字符串。

【描述1.D.5】RedirectServlet.java

    public class RedirectServlet extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            response.setContentType("text/html; charset=GBK");
            PrintWriter out = response.getWriter();
            request.setAttribute("test","helloworld");
            out.println("重定向前");
            response.sendRedirect(request.getContextPath() + "/myservlet ");
            out.println("重定向后");
        }
    }

上述代碼中,調(diào)用了setAttribute()方法把test屬性值helloworld存儲(chǔ)到request對(duì)象中。

(2)改寫MyServlet,獲取request對(duì)象中的test屬性值。

【描述1.D.5】MyServlet.java

    public class MyServlet extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            // 設(shè)置響應(yīng)到客戶端的文本類型為HTML
            response.setContentType("text/html; charset=GBK");
            String test =(String)request.getAttribute("test");
            // 獲取輸出流
            PrintWriter out = response.getWriter();
            out.println("重定向和請(qǐng)求轉(zhuǎn)發(fā)");
            out.println(test);
        }
    }

上述代碼中,從request對(duì)象中獲取test屬性值。

啟動(dòng)Tomcat,在IE中訪問http://localhost:8080/ch01/redirect,運(yùn)行結(jié)果如下:

    重定向和請(qǐng)求轉(zhuǎn)發(fā)null

由此可知,在MyServlet中的request對(duì)象中并沒有獲得RedirectServlet中request對(duì)象設(shè)置的值。

(3)改寫ForwardServlet,獲取request對(duì)象中的test屬性值。

【描述1.D.5】ForwardServlet.java

    // 請(qǐng)求轉(zhuǎn)發(fā)
    public class ForwardServlet extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            response.setContentType("text/html; charset=GBK");
            request.setAttribute("test","helloworld");
            PrintWriter out = response.getWriter();
            out.println("請(qǐng)求轉(zhuǎn)發(fā)前");
            RequestDispatcher rd = request.getRequestDispatcher("/myservlet");
            rd.forward(request, response);
            out.println("請(qǐng)求轉(zhuǎn)發(fā)后");
        }
    }

上述代碼中,從request對(duì)象中獲取test屬性值。

啟動(dòng)Tomcat,在IE中訪問http://localhost:8080/ch01/forward,運(yùn)行結(jié)果如下:

    重定向和請(qǐng)求轉(zhuǎn)發(fā)helloworld

由此可知,在MyServlet中的request對(duì)象中獲得了ForwardServlet的request對(duì)象設(shè)置的值。

通過對(duì)上述示例的運(yùn)行結(jié)果進(jìn)行比較,forward()和sendRedirect()兩者的區(qū)別總結(jié)如下:

■ forward()只能將請(qǐng)求轉(zhuǎn)發(fā)給同一個(gè)Web應(yīng)用中的組件,而sendRedirect()方法不僅可以重定向到當(dāng)前應(yīng)用程序中的其他資源,還可以重定向到其他站點(diǎn)中的資源。如果傳給sendRedirect()方法的相對(duì)URL以“/”開頭,它是相對(duì)于整個(gè)Web站點(diǎn)的根目錄;如果創(chuàng)建RequestDispatcher對(duì)象時(shí)指定的相對(duì)URL以“/”開頭,它是相對(duì)于當(dāng)前Web應(yīng)用程序的根目錄。

■ sendRedirect()方法重定向的訪問過程結(jié)束后,瀏覽器地址欄中顯示的URL會(huì)發(fā)生改變,由初始的URL地址變成重定向的目標(biāo)URL;而調(diào)用forward()方法的請(qǐng)求轉(zhuǎn)發(fā)過程結(jié)束后,瀏覽器地址欄保持初始的URL地址不變。

■ forward()方法的調(diào)用者與被調(diào)用者之間共享相同的request對(duì)象和response對(duì)象,它們屬于同一個(gè)請(qǐng)求和響應(yīng)過程;而sendRedirect()方法調(diào)用者和被調(diào)用者使用各自的request對(duì)象和response對(duì)象,它們屬于兩個(gè)獨(dú)立的請(qǐng)求和響應(yīng)過程。

1.7 小結(jié)

通過本章的學(xué)習(xí),學(xué)生應(yīng)該能夠?qū)W會(huì):

■ 動(dòng)態(tài)網(wǎng)站開發(fā)技術(shù)有Servlet、JSP、PHP、ASP、ASP.NET和CGI等。

■ Servlet是運(yùn)行在服務(wù)器端的Java程序,內(nèi)嵌HTML。

■ Servlet生命周期的三個(gè)方法分別是init()、service()和destroy()。

■ Servlet處理GET和POST請(qǐng)求時(shí)分別使用doGet()和doPost()方法進(jìn)行處理。

■ HttpServletRequest的getParameter(“參數(shù)名稱”)獲取表單、URL參數(shù)值。

■ HttpServletResponse的getWriter()獲取向客戶端發(fā)送信息的輸出流。

■ HttpServletRequest的getHeader(“報(bào)頭名稱”)獲取相關(guān)報(bào)頭信息。

■ 請(qǐng)求轉(zhuǎn)發(fā)和重定向都可以使瀏覽器獲得另外一個(gè)URL所指向的資源。

■ 請(qǐng)求轉(zhuǎn)發(fā)通常由RequestDispatcher接口的forward()方法實(shí)現(xiàn),轉(zhuǎn)發(fā)前后共享同一個(gè)請(qǐng)求對(duì)象。

■ 重定向由HttpServletResponse接口的sendRedirect()方法實(shí)現(xiàn),重定向不共享同一個(gè)請(qǐng)求對(duì)象。

練習(xí)

1. 下列選項(xiàng)中屬于動(dòng)態(tài)網(wǎng)站技術(shù)的是______。(多選)

A. PHP

B. ASP

C. JavaScript

D. JSP

2. 下列關(guān)于Servlet的說法正確的是______。(多選)

A. Servlet是一種動(dòng)態(tài)網(wǎng)站技術(shù)

B. Servlet運(yùn)行在服務(wù)器端

C. Servlet針對(duì)每個(gè)請(qǐng)求使用1個(gè)進(jìn)程來處理

D. Servlet與普通的Java類一樣,可以直接運(yùn)行,不需要環(huán)境支持

3. 下列關(guān)于Servlet的編寫方式正確的是______。(多選)

A. 必須是HttpServlet的子類

B. 通常需要覆蓋doGet()和doPost()方法或其中之一

C. 通常需要覆蓋service()方法

D. 通常需要在web.xml文件中聲明<servlet>和<servlet-mapping>兩個(gè)元素

4. 下列關(guān)于Servlet生命周期的說法正確的是______。(多選)

A. 構(gòu)造方法只會(huì)調(diào)用一次,在容器啟動(dòng)時(shí)調(diào)用

B. init()方法只會(huì)調(diào)用一次,在第一次請(qǐng)求此Servlet時(shí)調(diào)用

C. service()方法在每次請(qǐng)求此Servlet時(shí)都會(huì)被調(diào)用

D. destroy()方法在每次請(qǐng)求完畢時(shí)會(huì)被調(diào)用

5. 下列方式中可以執(zhí)行TestServlet(路徑為 /test)的doPost()方法的是______。(多選)

A. 在IE中直接訪問http://localhost:8080/網(wǎng)站名/test

B. <form action="/網(wǎng)站名/test"> 提交此表單

C. <form action="/網(wǎng)站名/test" method="post"> 提交此表單

D. <form id="form1">,在JavaScript中執(zhí)行下述代碼:

    document.getElementById("form1").action="/網(wǎng)站名/test";
    document.getElementById("form1").method="post";
    document.getElementById("form1").submit();

6. 針對(duì)下述JSP頁面,在Servlet中需要得到用戶選擇的愛好的數(shù)量,最合適的代碼是______。

    <input type="checkbox" name="aihao" value="1"/>游戲<br/>
    <input type="checkbox" name="aihao" value="2"/>運(yùn)動(dòng)<br/>
    <input type="checkbox" name="aihao" value="3"/>棋牌<br/>
    <input type="checkbox" name="aihao" value="4"/>美食<br/>

A. request.getParameter("aihao").length

B. request.getParameter("aihao").size()

C. request.getParameterValues("aihao").length

D. request.getParameterValues("aihao").size()

7. 用戶使用POST方式提交的數(shù)據(jù)中存在漢字(使用GBK字符集),在Servlet中需要使用下述______語句處理。

A. request.setCharacterEncoding("GBK");

B. request.setContentType("text/html;charset=GBK");

C. response.setCharacterEncoding("GBK");

D. response.setContentType("text/html;charset=GBK");

8. 簡述Servlet的生命周期。Servlet在第一次和第二次被訪問時(shí),其生命周期方法的執(zhí)行有何區(qū)別?

9. 簡述轉(zhuǎn)發(fā)和重定向兩種頁面跳轉(zhuǎn)方式的區(qū)別,在Servlet中分別使用什么方法實(shí)現(xiàn)?

主站蜘蛛池模板: 正蓝旗| 左云县| 万盛区| 门头沟区| 济阳县| 离岛区| 冀州市| 南康市| 建宁县| 环江| 山西省| 年辖:市辖区| 古田县| 土默特左旗| 怀安县| 星子县| 龙口市| 镇康县| 井陉县| 柞水县| 镇平县| 健康| 天峻县| 明溪县| 永德县| 济南市| 长海县| 怀来县| 汽车| 固安县| 卢龙县| 晋中市| 忻州市| 天门市| 肥西县| 临清市| 新平| 民乐县| 南汇区| 石泉县| 白沙|