<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 120,  comments - 19,  trackbacks - 0
    一、Servlet和JSP概述
    ? 作 者 : 仙人掌工作室
    ??
    ??
    ?    1.1 Java Servlet及其特點(diǎn)
    ??
    ?    Servlet是Java技術(shù)對(duì)CGI編程的回答。Servlet程序在服務(wù)器端運(yùn)行,動(dòng)態(tài)地生成Web頁面。與傳統(tǒng)的CGI和許多其他類似CGI的技術(shù)相比,Java Servlet具有更高的效率,更容易使用,功能更強(qiáng)大,具有更好的可移植性,更節(jié)省投資(更重要的是, Servlet程序員收入要比Perl程序員高:-):
    ??
    ? 高效。
    ??
    ? 在傳統(tǒng)的CGI中,每個(gè)請(qǐng)求都要啟動(dòng)一個(gè)新的進(jìn)程,如果CGI程序本身的執(zhí)行時(shí)間較短,啟動(dòng)進(jìn)程所需要的開銷很可能反而超過實(shí)際執(zhí)行時(shí)間。而在Servlet中,每個(gè)請(qǐng)求由一個(gè)輕量級(jí)的Java線程處理(而不是重量級(jí)的操作系統(tǒng)進(jì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ù)據(jù)庫連接池之類的功能很容易實(shí)現(xiàn)。
    ??
    ??
    ? 可移植性好。
    ??
    ? Servlet用Java編寫,Servlet API具有完善的標(biāo)準(zhǔn)。因此,為I-Planet Enterprise Server寫的Servlet無需任何實(shí)質(zhì)上的改動(dòng)即可移植到Apache、Microsoft IIS或者WebStar。幾乎所有的主流服務(wù)器都直接或通過插件支持Servlet。
    ??
    ??
    ? 節(jié)省投資。
    ??
    ? 不僅有許多廉價(jià)甚至免費(fèi)的Web服務(wù)器可供個(gè)人或小規(guī)模網(wǎng)站使用,而且對(duì)于現(xiàn)有的服務(wù)器,如果它不支持Servlet的話,要加上這部分功能也往往是免費(fèi)的(或只需要極少的投資)。
    ?    1.2 JSP及其特點(diǎn)
    ??
    ?    JavaServer Pages(JSP)是一種實(shí)現(xiàn)普通靜態(tài)HTML和動(dòng)態(tài)HTML混合編碼的技術(shù),有關(guān)JSP基礎(chǔ)概念的說明請(qǐng)參見《JSP技術(shù)簡(jiǎn)介 》。
    ??
    ?    許多由CGI程序生成的頁面大部分仍舊是靜態(tài)HTML,動(dòng)態(tài)內(nèi)容只在頁面中有限的幾個(gè)部分出現(xiàn)。但是包括Servlet在內(nèi)的大多數(shù)CGI技術(shù)及其變種,總是通過程序生成整個(gè)頁面。JSP使得我們可以分別創(chuàng)建這兩個(gè)部分。例如,下面就是一個(gè)簡(jiǎn)單的JSP頁面:
    ? <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
    ? <HTML>
    ? <HEAD><TITLE>歡迎訪問網(wǎng)上商店</TITLE></HEAD>
    ? <BODY>
    ? <H1>歡迎</H1>
    ? <SMALL>歡迎,
    ? <!-- 首次訪問的用戶名字為"New User" -->
    ? <% out.println(Utils.getUserNameFromCookie(request)); %>
    ? 要設(shè)置帳號(hào)信息,請(qǐng)點(diǎn)擊
    ? <A HREF="Account-Settings.html">這里</A></SMALL>
    ? <P>
    ? 頁面的其余內(nèi)容。.
    ? </BODY></HTML>
    ??
    ??
    ??
    ?    下面是JSP和其他類似或相關(guān)技術(shù)的一個(gè)簡(jiǎn)單比較:
    ??
    ? JSP和Active Server Pages(ASP)相比
    ??
    ? Microsoft的ASP是一種和JSP類似的技術(shù)。JSP和ASP相比具有兩方面的優(yōu)點(diǎn)。首先,動(dòng)態(tài)部分用Java編寫,而不是VB Script或其他Microsoft語言,不僅功能更強(qiáng)大而且更易于使用。第二,JSP應(yīng)用可以移植到其他操作系統(tǒng)和非Microsoft的Web服務(wù)器上。
    ??
    ??
    ? JSP和純Servlet相比
    ??
    ? JSP并沒有增加任何本質(zhì)上不能用Servlet實(shí)現(xiàn)的功能。但是,在JSP中編寫靜態(tài)HTML更加方便,不必再用 println語句來輸出每一行HTML代碼。更重要的是,借助內(nèi)容和外觀的分離,頁面制作中不同性質(zhì)的任務(wù)可以方便地分開:比如,由頁面設(shè)計(jì)專家進(jìn)行HTML設(shè)計(jì),同時(shí)留出供Servlet程序員插入動(dòng)態(tài)內(nèi)容的空間。
    ??
    ??
    ? JSP和服務(wù)器端包含(Server-Side Include,SSI)相比
    ??
    ? SSI是一種受到廣泛支持的在靜態(tài)HTML中引入外部代碼的技術(shù)。JSP在這方面的支持更為完善,因?yàn)樗梢杂肧ervlet而不是獨(dú)立的程序來生成動(dòng)態(tài)內(nèi)容。另外,SSI實(shí)際上只用于簡(jiǎn)單的包含,而不是面向那些能夠處理表單數(shù)據(jù)、訪問數(shù)據(jù)庫的“真正的”程序。
    ??
    ??
    ? JSP和JavaScript相比
    ??
    ? JavaScript能夠在客戶端動(dòng)態(tài)地生成HTML。雖然JavaScript很有用,但它只能處理以客戶端環(huán)境為基礎(chǔ)的動(dòng)態(tài)信息。除了Cookie之外,HTTP狀態(tài)和表單提交數(shù)據(jù)對(duì)JavaScript來說都是不可用的。另外,由于是在客戶端運(yùn)行,JavaScript不能訪問服務(wù)器端資源,比如數(shù)據(jù)庫、目錄信息等等。
    ?2.1 安裝Servlet和JSP開發(fā)工具
    ??
    ?    要學(xué)習(xí)Servlet和JSP開發(fā),首先你必須準(zhǔn)備一個(gè)符合Java Servlet 2.1/2.2和JavaServer Pages1.0/1.1規(guī)范的開發(fā)環(huán)境。Sun提供免費(fèi)的JavaServer Web Development Kit(JSWDK),可以從http://java.sun.com/products/servlet/ 下載。
    ??
    ?    安裝好JSWDK之后,你還要告訴javac,在編譯文件的時(shí)候到哪里去尋找Servlet和JSP類。JSWDK安裝指南對(duì)此有詳細(xì)說明,但主要就是把servlet.jar和jsp.jar加入CLASSPATH。CLASSPATH是一個(gè)指示Java如何尋找類文件的環(huán)境變量,如果不設(shè)置CLASSPATH,Java在當(dāng)前目錄和標(biāo)準(zhǔn)系統(tǒng)庫中尋找類;如果你自己設(shè)置了CLASSPATH,不要忘記包含當(dāng)前目錄(即在CLASSPATH中包含“.”)。
    ??
    ?    另外,為了避免和其他開發(fā)者安裝到同一Web服務(wù)器上的Servlet產(chǎn)生命名沖突,最好把自己的Servlet放入包里面。此時(shí),把包層次結(jié)構(gòu)中的頂級(jí)目錄也加入CLASSPATH會(huì)帶來不少方便。請(qǐng)參見下文具體說明。
    ??
    ?    2.2 安裝支持Servlet的Web服務(wù)器
    ??
    ?    除了開發(fā)工具之外,你還要安裝一個(gè)支持Java Servlet的Web服務(wù)器,或者在現(xiàn)有的Web服務(wù)器上安裝Servlet軟件包。如果你使用的是最新的Web服務(wù)器或應(yīng)用服務(wù)器,很可能它已經(jīng)有了所有必需的軟件。請(qǐng)查看Web服務(wù)器的文檔,或訪問http://java.sun.com/products/servlet/industry.html 查看支持Servlet的服務(wù)器軟件清單。
    ??
    ?    雖然最終運(yùn)行Servlet的往往是商業(yè)級(jí)的服務(wù)器,但是開始學(xué)習(xí)的時(shí)候,用一個(gè)能夠在臺(tái)式機(jī)上運(yùn)行的免費(fèi)系統(tǒng)進(jìn)行開發(fā)和測(cè)試也足夠了。下面是幾種當(dāng)前最受歡迎的產(chǎn)品。
    ??
    ? Apache Tomcat.
    ??
    ? Tomcat是Servlet 2.2和JSP 1.1規(guī)范的官方參考實(shí)現(xiàn)。Tomcat既可以單獨(dú)作為小型Servlet、JSP測(cè)試服務(wù)器,也可以集成到Apache Web服務(wù)器。直到2000年早期,Tomcat還是唯一的支持Servlet 2.2和JSP 1.1規(guī)范的服務(wù)器,但已經(jīng)有許多其它服務(wù)器宣布提供這方面的支持。
    ??
    ? Tomcat和Apache一樣是免費(fèi)的。不過,快速、穩(wěn)定的Apache服務(wù)器安裝和配置起來有點(diǎn)麻煩,Tomcat也有同樣的缺點(diǎn)。和其他商業(yè)級(jí)Servlet引擎相比,配置Tomcat的工作量顯然要多一點(diǎn)。具體請(qǐng)參見http://jakarta.apache.org/ 。
    ??
    ??
    ? JavaServer Web Development Kit (JSWDK).
    ??
    ? JSWDK是Servlet 2.1和JSP 1.0的官方參考實(shí)現(xiàn)。把Servlet和JSP應(yīng)用部署到正式運(yùn)行它們的服務(wù)器之前,JSWDK可以單獨(dú)作為小型的Servlet、JSP測(cè)試服務(wù)器。JSWDK也是免費(fèi)的,而且具有很好的穩(wěn)定性,但它的安裝和配置也較為復(fù)雜。具體請(qǐng)參見http://java.sun.com/products/servlet/download.html 。
    ??
    ??
    ? Allaire JRun.
    ??
    ? JRun是一個(gè)Servlet和JSP引擎,它可以集成到Netscape Enterprise或FastTrack Server、IIS、Microsoft Personal Web Server、版本較低的Apache、O'eilly的WebSite或者StarNine Web STAR。最多支持5個(gè)并發(fā)連接的限制版本是免費(fèi)的,商業(yè)版本中不存在這個(gè)限制,而且增加了遠(yuǎn)程管理控制臺(tái)之類的功能。具體請(qǐng)參見http://www.allaire.com/products/jrun/
    ??
    ??
    ? New Atlanta 的ServletExec
    ??
    ? ServletExec是一個(gè)快速的Servlet和JSP引擎,它可以集成到大多數(shù)流行的Web服務(wù)器,支持平臺(tái)包括Solaris、Windows、MacOS、HP-UX和Linux。ServletExec可以免費(fèi)下載和使用,但許多高級(jí)功能和管理工具只有在購買了許可之后才可以使用。New Atlanta還提供一個(gè)免費(fèi)的Servlet調(diào)試器,該調(diào)試器可以在許多流行的Java IDE下工作。具體請(qǐng)參見http://newatlanta.com/
    ??
    ??
    ? Gefion的LiteWebServer (LWS)
    ??
    ? LWS是一個(gè)支持Servlet 2.2和JSP 1.1的免費(fèi)小型Web服務(wù)器。 Gefion還有一個(gè)免費(fèi)的WAICoolRunner插件,利用該插件可以為Netscape FastTrack和Enterprise Server增加Servlet 2.2和JSP 1.1支持。具體請(qǐng)參見http://www.gefionsoftware.com/ 。
    ??
    ??
    ? Sun的Java Web Server.
    ??
    ? 該服務(wù)器全部用Java寫成,而且是首先提供Servlet 2.1和JSP 1.0規(guī)范完整支持的Web服務(wù)器之一。雖然Sun現(xiàn)在已轉(zhuǎn)向Netscape/I-Planet Server,不再發(fā)展Java Web Server,但它仍舊是一個(gè)廣受歡迎的Servlet、JSP學(xué)習(xí)平臺(tái)。要得到免費(fèi)試用版本,請(qǐng)?jiān)L問http://www.sun.com/software/jwebserver/try/
    ?
    3.1 Servlet基本結(jié)構(gòu)
    ??
    ?    下面的代碼顯示了一個(gè)簡(jiǎn)單Servlet的基本結(jié)構(gòu)。該Servlet處理的是GET請(qǐng)求,所謂的GET請(qǐng)求,如果你不熟悉HTTP,可以把它看成是當(dāng)用戶在瀏覽器地址欄輸入U(xiǎn)RL、點(diǎn)擊Web頁面中的鏈接、提交沒有指定METHOD的表單時(shí)瀏覽器所發(fā)出的請(qǐng)求。Servlet也可以很方便地處理POST請(qǐng)求。POST請(qǐng)求是提交那些指定了METHOD=“POST”的表單時(shí)所發(fā)出的請(qǐng)求,具體請(qǐng)參見稍后幾節(jié)的討論。
    ? import java.io.*;
    ? import javax.servlet.*;
    ? import javax.servlet.http.*;
    ??
    ? public class SomeServlet extends HttpServlet {
    ?? public void doGet(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ??
    ?? // 使用“request”讀取和請(qǐng)求有關(guān)的信息(比如Cookies)
    ?? // 和表單數(shù)據(jù)
    ??
    ?? // 使用“response”指定HTTP應(yīng)答狀態(tài)代碼和應(yīng)答頭
    ?? // (比如指定內(nèi)容類型,設(shè)置Cookie)
    ??
    ?? PrintWriter out = response.getWriter();
    ?? // 使用 "out"把應(yīng)答內(nèi)容發(fā)送到瀏覽器
    ?? }
    ? }
    ??
    ??
    ??
    ??
    ?    如果某個(gè)類要成為Servlet,則它應(yīng)該從HttpServlet 繼承,根據(jù)數(shù)據(jù)是通過GET還是POST發(fā)送,覆蓋doGet、doPost方法之一或全部。doGet和doPost方法都有兩個(gè)參數(shù),分別為HttpServletRequest 類型和HttpServletResponse 類型。HttpServletRequest提供訪問有關(guān)請(qǐng)求的信息的方法,例如表單數(shù)據(jù)、HTTP請(qǐng)求頭等等。HttpServletResponse除了提供用于指定HTTP應(yīng)答狀態(tài)(200,404等)、應(yīng)答頭(Content-Type,Set-Cookie等)的方法之外,最重要的是它提供了一個(gè)用于向客戶端發(fā)送數(shù)據(jù)的PrintWriter 。對(duì)于簡(jiǎn)單的Servlet來說,它的大部分工作是通過println語句生成向客戶端發(fā)送的頁面。
    ??
    ?    注意doGet和doPost拋出兩個(gè)異常,因此你必須在聲明中包含它們。另外,你還必須導(dǎo)入java.io包(要用到PrintWriter等類)、javax.servlet包(要用到HttpServlet等類)以及javax.servlet.http包(要用到HttpServletRequest類和HttpServletResponse類)。
    ??
    ?    最后,doGet和doPost這兩個(gè)方法是由service方法調(diào)用的,有時(shí)你可能需要直接覆蓋service方法,比如Servlet要處理GET和POST兩種請(qǐng)求時(shí)。
    ??
    ?    3.2 輸出純文本的簡(jiǎn)單Servlet
    ??
    ?    下面是一個(gè)輸出純文本的簡(jiǎn)單Servlet。
    ??
    ?    3.2.1 HelloWorld.java
    ? package hall;
    ??
    ? import java.io.*;
    ? import javax.servlet.*;
    ? import javax.servlet.http.*;
    ??
    ? public class HelloWorld extends HttpServlet {
    ?? public void doGet(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ?? PrintWriter out = response.getWriter();
    ?? out.println("Hello World");
    ?? }
    ? }
    ??
    ??
    ??
    ??
    ?    3.2.2 Servlet的編譯和安裝
    ??
    ?    不同的Web服務(wù)器上安裝Servlet的具體細(xì)節(jié)可能不同,請(qǐng)參考Web服務(wù)器文檔了解更權(quán)威的說明。假定使用Java Web Server(JWS)2.0,則Servlet應(yīng)該安裝到JWS安裝目錄的servlets子目錄下。在本文中,為了避免同一服務(wù)器上不同用戶的Servlet命名沖突,我們把所有Servlet都放入一個(gè)獨(dú)立的包hall中;如果你和其他人共用一個(gè)服務(wù)器,而且該服務(wù)器沒有“虛擬服務(wù)器”機(jī)制來避免這種命名沖突,那么最好也使用包。把Servlet放入了包hall之后,HelloWorld.java實(shí)際上是放在servlets目錄的hall子目錄下。
    ??
    ?    大多數(shù)其他服務(wù)器的配置方法也相似,除了JWS之外,本文的Servlet和JSP示例已經(jīng)在BEA WebLogic和IBM WebSphere 3.0下經(jīng)過測(cè)試。WebSphere具有優(yōu)秀的虛擬服務(wù)器機(jī)制,因此,如果只是為了避免命名沖突的話并非一定要用包。
    ??
    ?    對(duì)于沒有使用過包的初學(xué)者,下面我們介紹編譯包里面的類的兩種方法。
    ??
    ?    一種方法是設(shè)置CLASSPATH,使其指向?qū)嶋H存放Servlet的目錄的上一級(jí)目錄(Servlet主目錄),然后在該目錄中按正常的方式編譯。例如,如果Servlet的主目錄是C:\JavaWebServer\servlets,包的名字(即主目錄下的子目錄名字)是hall,在Windows下,編譯過程如下:
    ?? DOS> set CLASSPATH=C:\JavaWebServer\servlets;%CLASSPATH%
    ?? DOS> cd C:\JavaWebServer\servlets\hall
    ?? DOS> javac YourServlet.java
    ??
    ??
    ??
    ?    第二種編譯包里面的Servlet的方法是進(jìn)入Servlet主目錄,執(zhí)行“javac directory\YourServlet.java”(Windows)或者“javac directory/YourServlet.java”(Unix)。例如,再次假定Servlet主目錄是C:\JavaWebServer\servlets,包的名字是hall,在Windows中編譯過程如下:
    ?? DOS> cd C:\JavaWebServer\servlets
    ?? DOS> javac hall\YourServlet.java
    ??
    ??
    ??
    ?    注意在Windows下,大多數(shù)JDK 1.1版本的javac要求目錄名字后面加反斜杠(\)。JDK1.2已經(jīng)改正這個(gè)問題,然而由于許多Web服務(wù)器仍舊使用JDK 1.1,因此大量的Servlet開發(fā)者仍舊在使用JDK 1.1。
    ??
    ?    最后,Javac還有一個(gè)高級(jí)選項(xiàng)用于支持源代碼和.class文件的分開放置,即你可以用javac的“-d”選項(xiàng)把.class文件安裝到Web服務(wù)器所要求的目錄。
    ??
    ?    3.2.3 運(yùn)行Servlet
    ??
    ?    在Java Web Server下,Servlet應(yīng)該放到JWS安裝目錄的servlets子目錄下,而調(diào)用Servlet的URL是http://host/servlet/ServletName。注意子目錄的名字是servlets(帶“s”),而URL使用的是“servlet”。由于HelloWorld Servlet放入包hall,因此調(diào)用它的URL應(yīng)該是http://host/servlet/hall.HelloWorld。在其他的服務(wù)器上,安裝和調(diào)用Servlet的方法可能略有不同。
    ??
    ?    大多數(shù)Web服務(wù)器還允許定義Servlet的別名,因此Servlet也可能用http://host/any-path/any-file.html形式的URL調(diào)用。具體如何進(jìn)行配置完全依賴于服務(wù)器類型,請(qǐng)參考服務(wù)器文檔了解細(xì)節(jié)。
    ??
    ?    3.3 輸出HTML的Servlet
    ??
    ?    大多數(shù)Servlet都輸出HTML,而不象上例一樣輸出純文本。要輸出HTML還有兩個(gè)額外的步驟要做:告訴瀏覽器接下來發(fā)送的是HTML;修改println語句構(gòu)造出合法的HTML頁面。
    ??
    ?    第一步通過設(shè)置Content-Type(內(nèi)容類型)應(yīng)答頭完成。一般地,應(yīng)答頭可以通過HttpServletResponse的setHeader方法設(shè)置,但由于設(shè)置內(nèi)容類型是一個(gè)很頻繁的操作,因此Servlet API提供了一個(gè)專用的方法setContentType。注意設(shè)置應(yīng)答頭應(yīng)該在通過PrintWriter發(fā)送內(nèi)容之前進(jìn)行。下面是一個(gè)實(shí)例:
    ??
    ?    HelloWWW.java
    ? package hall;
    ??
    ? import java.io.*;
    ? import javax.servlet.*;
    ? import javax.servlet.http.*;
    ??
    ? public class HelloWWW extends HttpServlet {
    ?? public void doGet(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ?? response.setContentType("text/html");
    ?? PrintWriter out = response.getWriter();
    ?? out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 " +
    ?? "Transitional//EN\">\n" +
    ?? "<HTML>\n" +
    ?? "<HEAD><TITLE>Hello WWW</TITLE></HEAD>\n" +
    ?? "<BODY>\n" +
    ?? "<H1>Hello WWW</H1>\n" +
    ?? "</BODY></HTML>");
    ?? }
    ? }
    ??
    ??
    ??
    ??
    ?    3.4 幾個(gè)HTML工具函數(shù)
    ??
    ?    通過println語句輸出HTML并不方便,根本的解決方法是使用JavaServer Pages(JSP)。然而,對(duì)于標(biāo)準(zhǔn)的Servlet來說,由于Web頁面中有兩個(gè)部分(DOCTYPE和HEAD)一般不會(huì)改變,因此可以用工具函數(shù)來封裝生成這些內(nèi)容的代碼。
    ??
    ?    雖然大多數(shù)主流瀏覽器都會(huì)忽略DOCTYPE行,但嚴(yán)格地說HTML規(guī)范是要求有DOCTYPE行的,它有助于HTML語法檢查器根據(jù)所聲明的HTML版本檢查HTML文檔合法性。在許多Web頁面中,HEAD部分只包含<TITLE>。雖然許多有經(jīng)驗(yàn)的編寫者都會(huì)在HEAD中包含許多META標(biāo)記和樣式聲明,但這里只考慮最簡(jiǎn)單的情況。
    ??
    ?    下面的Java方法只接受頁面標(biāo)題為參數(shù),然后輸出頁面的DOCTYPE、HEAD、TITLE部分。清單如下:
    ??
    ?    ServletUtilities.java
    ? package hall;
    ??
    ? public class ServletUtilities {
    ?? public static final String DOCTYPE =
    ?? "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">";
    ??
    ?? public static String headWithTitle(String title) {
    ?? return(DOCTYPE + "\n" +
    ?? "<HTML>\n" +
    ?? "<HEAD><TITLE>" + title + "</TITLE></HEAD>\n");
    ?? }
    ??
    ?? // 其他工具函數(shù)的代碼在本文后面介紹
    ? }
    ??
    ??
    ??
    ??
    ?    HelloWWW2.java
    ??
    ?    下面是應(yīng)用了ServletUtilities之后重寫HelloWWW類得到的HelloWWW2:
    ? package hall;
    ??
    ? import java.io.*;
    ? import javax.servlet.*;
    ? import javax.servlet.http.*;
    ??
    ? public class HelloWWW2 extends HttpServlet {
    ?? public void doGet(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ?? response.setContentType("text/html");
    ?? PrintWriter out = response.getWriter();
    ?? out.println(ServletUtilities.headWithTitle("Hello WWW") +
    ?? "<BODY>\n" +
    ?? "<H1>Hello WWW</H1>\n" +
    ?? "</BODY></HTML>");
    ?? }
    ? }
    4.1 表單數(shù)據(jù)概述
    ??
    ?    如果你曾經(jīng)使用過Web搜索引擎,或者瀏覽過在線書店、股票價(jià)格、機(jī)票信息,或許會(huì)留意到一些古怪的URL,比如“http://host/path?user=Marty+Hall&origin=bwi&dest=lax”。這個(gè)URL中位于問號(hào)后面的部分,即“user=Marty+Hall&origin=bwi&dest=lax”,就是表單數(shù)據(jù),這是將Web頁面數(shù)據(jù)發(fā)送給服務(wù)器程序的最常用方法。對(duì)于GET請(qǐng)求,表單數(shù)據(jù)附加到URL的問號(hào)后面(如上例所示);對(duì)于POST請(qǐng)求,表單數(shù)據(jù)用一個(gè)單獨(dú)的行發(fā)送給服務(wù)器。
    ??
    ?    以前,從這種形式的數(shù)據(jù)提取出所需要的表單變量是CGI編程中最麻煩的事情之一。首先,GET請(qǐng)求和POST請(qǐng)求的數(shù)據(jù)提取方法不同:對(duì)于GET請(qǐng)求,通常要通過QUERY_STRING環(huán)境變量提取數(shù)據(jù);對(duì)于POST請(qǐng)求,則一般通過標(biāo)準(zhǔn)輸入提取數(shù)據(jù)。第二,程序員必須負(fù)責(zé)在“&”符號(hào)處截?cái)嘧兞棵?變量值對(duì),再分離出變量名字(等號(hào)左邊)和變量值(等號(hào)右邊)。第三,必須對(duì)變量值進(jìn)行URL反編碼操作。因?yàn)榘l(fā)送數(shù)據(jù)的時(shí)候,字母和數(shù)字以原來的形式發(fā)送,但空格被轉(zhuǎn)換成加號(hào),其他字符被轉(zhuǎn)換成“%XX”形式,其中XX是十六進(jìn)制表示的字符ASCII(或者ISO Latin-1)編碼值。例如,如果HTML表單中名為“users”的域值為“~hall, ~gates, and ~mcnealy”,則實(shí)際向服務(wù)器發(fā)送的數(shù)據(jù)為“users=%7Ehall%2C+%7Egates%2C+and+%7Emcnealy”。最后,即第四個(gè)導(dǎo)致解析表單數(shù)據(jù)非常困難的原因在于,變量值既可能被省略(如“param1=val1&param2=&param3=val3”),也有可能一個(gè)變量擁有一個(gè)以上的值,即同一個(gè)變量可能出現(xiàn)一次以上(如“param1=val1&param2=val2&param1=val3”)。
    ??
    ?    Java Servlet的好處之一就在于所有上述解析操作都能夠自動(dòng)完成。只需要簡(jiǎn)單地調(diào)用一下HttpServletRequest的getParameter方法、在調(diào)用參數(shù)中提供表單變量的名字(大小寫敏感)即可,而且GET請(qǐng)求和POST請(qǐng)求的處理方法完全相同。
    ??
    ?    getParameter方法的返回值是一個(gè)字符串,它是參數(shù)中指定的變量名字第一次出現(xiàn)所對(duì)應(yīng)的值經(jīng)反編碼得到得字符串(可以直接使用)。如果指定的表單變量存在,但沒有值,getParameter返回空字符串;如果指定的表單變量不存在,則返回null。如果表單變量可能對(duì)應(yīng)多個(gè)值,可以用getParameterValues來取代getParameter。getParameterValues能夠返回一個(gè)字符串?dāng)?shù)組。
    ??
    ?    最后,雖然在實(shí)際應(yīng)用中Servlet很可能只會(huì)用到那些已知名字的表單變量,但在調(diào)試環(huán)境中,獲得完整的表單變量名字列表往往是很有用的,利用getParamerterNames方法可以方便地實(shí)現(xiàn)這一點(diǎn)。getParamerterNames返回的是一個(gè)Enumeration,其中的每一項(xiàng)都可以轉(zhuǎn)換為調(diào)用getParameter的字符串。
    ??
    ?    4.2 實(shí)例:讀取三個(gè)表單變量
    ??
    ?    下面是一個(gè)簡(jiǎn)單的例子,它讀取三個(gè)表單變量param1、param2和param3,并以HTML列表的形式列出它們的值。請(qǐng)注意,雖然在發(fā)送應(yīng)答內(nèi)容之前必須指定應(yīng)答類型(包括內(nèi)容類型、狀態(tài)以及其他HTTP頭信息),但Servlet對(duì)何時(shí)讀取請(qǐng)求內(nèi)容卻沒有什么要求。
    ??
    ?    另外,我們也可以很容易地把Servlet做成既能處理GET請(qǐng)求,也能夠處理POST請(qǐng)求,這只需要在doPost方法中調(diào)用doGet方法,或者覆蓋service方法(service方法調(diào)用doGet、doPost、doHead等方法)。在實(shí)際編程中這是一種標(biāo)準(zhǔn)的方法,因?yàn)樗恍枰苌俚念~外工作,卻能夠增加客戶端編碼的靈活性。
    ??
    ?    如果你習(xí)慣用傳統(tǒng)的CGI方法,通過標(biāo)準(zhǔn)輸入讀取POST數(shù)據(jù),那么在Servlet中也有類似的方法,即在HttpServletRequest上調(diào)用getReader或者getInputStream,但這種方法對(duì)普通的表單變量來說太麻煩。然而,如果是要上載文件,或者POST數(shù)據(jù)是通過專門的客戶程序而不是HTML表單發(fā)送,那么就要用到這種方法。
    ??
    ?    注意用第二種方法讀取POST數(shù)據(jù)時(shí),不能再用getParameter來讀取這些數(shù)據(jù)。
    ??
    ?    ThreeParams.java
    ? package hall;
    ??
    ? import java.io.*;
    ? import javax.servlet.*;
    ? import javax.servlet.http.*;
    ? import java.util.*;
    ??
    ? public class ThreeParams extends HttpServlet {
    ?? public void doGet(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ?? response.setContentType("text/html");
    ?? PrintWriter out = response.getWriter();
    ?? String title = "讀取三個(gè)請(qǐng)求參數(shù)";
    ?? out.println(ServletUtilities.headWithTitle(title) +
    ?? "<BODY>\n" +
    ?? "<H1 ALIGN=CENTER>" + title + "</H1>\n" +
    ?? "<UL>\n" +
    ?? " <LI>param1: "
    ?? + request.getParameter("param1") + "\n" +
    ?? " <LI>param2: "
    ?? + request.getParameter("param2") + "\n" +
    ?? " <LI>param3: "
    ?? + request.getParameter("param3") + "\n" +
    ?? "</UL>\n" +
    ?? "</BODY></HTML>");
    ?? }
    ??
    ?? public void doPost(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ?? doGet(request, response);
    ?? }
    ? }
    ??
    ??
    ??
    ??
    ?    4.3 實(shí)例:輸出所有的表單數(shù)據(jù)
    ??
    ?    下面這個(gè)例子尋找表單所發(fā)送的所有變量名字,并把它們放入表格中,沒有值或者有多個(gè)值的變量都突出顯示。
    ??
    ?    首先,程序通過HttpServletRequest的getParameterNames方法得到所有的變量名字,getParameterNames返回的是一個(gè)Enumeration。接下來,程序用循環(huán)遍歷這個(gè)Enumeration,通過hasMoreElements確定何時(shí)結(jié)束循環(huán),利用nextElement得到Enumeration中的各個(gè)項(xiàng)。由于nextElement返回的是一個(gè)Object,程序把它轉(zhuǎn)換成字符串后再用這個(gè)字符串來調(diào)用getParameterValues。
    ??
    ?    getParameterValues返回一個(gè)字符串?dāng)?shù)組,如果這個(gè)數(shù)組只有一個(gè)元素且等于空字符串,說明這個(gè)表單變量沒有值,Servlet以斜體形式輸出“No Value”;如果數(shù)組元素個(gè)數(shù)大于1,說明這個(gè)表單變量有多個(gè)值,Servlet以HTML列表形式輸出這些值;其他情況下Servlet直接把變量值放入表格。
    ??
    ?    ShowParameters.java
    ??
    ?    注意,ShowParameters.java用到了前面介紹過的ServletUtilities.java。
    ? package hall;
    ??
    ? import java.io.*;
    ? import javax.servlet.*;
    ? import javax.servlet.http.*;
    ? import java.util.*;
    ??
    ? public class ShowParameters extends HttpServlet {
    ?? public void doGet(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ?? response.setContentType("text/html");
    ?? PrintWriter out = response.getWriter();
    ?? String title = "讀取所有請(qǐng)求參數(shù)";
    ?? out.println(ServletUtilities.headWithTitle(title) +
    ?? "<BODY BGCOLOR=\"#FDF5E6\">\n" +
    ?? "<H1 ALIGN=CENTER>" + title + "</H1>\n" +
    ?? "<TABLE BORDER=1 ALIGN=CENTER>\n" +
    ?? "<TR BGCOLOR=\"#FFAD00\">\n" +
    ?? "<TH>參數(shù)名字<TH>參數(shù)值");
    ?? Enumeration paramNames = request.getParameterNames();
    ?? while(paramNames.hasMoreElements()) {
    ?? String paramName = (String)paramNames.nextElement();
    ?? out.println("<TR><TD>" + paramName + "\n<TD>");
    ?? String[] paramValues = request.getParameterValues(paramName);
    ?? if (paramValues.length == 1) {
    ?? String paramValue = paramValues[0];
    ?? if (paramValue.length() == 0)
    ?? out.print("<I>No Value</I>");
    ?? else
    ?? out.print(paramValue);
    ?? } else {
    ?? out.println("<UL>");
    ?? for(int i=0; i<paramValues.length; i++) {
    ?? out.println("<LI>" + paramValues[i]);
    ?? }
    ?? out.println("</UL>");
    ?? }
    ?? }
    ?? out.println("</TABLE>\n</BODY></HTML>");
    ?? }
    ??
    ?? public void doPost(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ?? doGet(request, response);
    ?? }
    ? }
    ??
    ??
    ??
    ??
    ?    測(cè)試表單
    ??
    ?    下面是向上述Servlet發(fā)送數(shù)據(jù)的表單PostForm.html。就像所有包含密碼輸入域的表單一樣,該表單用POST方法發(fā)送數(shù)據(jù)。我們可以看到,在Servlet中同時(shí)實(shí)現(xiàn)doGet和doPost這兩種方法為表單制作帶來了方便。
    ? <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
    ? <HTML>
    ? <HEAD>
    ?? <TITLE>示例表單</TITLE>
    ? </HEAD>
    ??
    ? <BODY BGCOLOR="#FDF5E6">
    ? <H1 ALIGN="CENTER">用POST方法發(fā)送數(shù)據(jù)的表單</H1>
    ??
    ? <FORM ACTION="/servlet/hall.ShowParameters"
    ?? METHOD="POST">
    ?? Item Number:
    ?? <INPUT TYPE="TEXT" NAME="itemNum"><BR>
    ?? Quantity:
    ?? <INPUT TYPE="TEXT" NAME="quantity"><BR>
    ?? Price Each:
    ?? <INPUT TYPE="TEXT" NAME="price" VALUE="$"><BR>
    ?? <HR>
    ?? First Name:
    ?? <INPUT TYPE="TEXT" NAME="firstName"><BR>
    ?? Last Name:
    ?? <INPUT TYPE="TEXT" NAME="lastName"><BR>
    ?? Middle Initial:
    ?? <INPUT TYPE="TEXT" NAME="initial"><BR>
    ?? Shipping Address:
    ?? <TEXTAREA NAME="address" ROWS=3 COLS=40></TEXTAREA><BR>
    ?? Credit Card:<BR>
    ?? <INPUT TYPE="RADIO" NAME="cardType"
    ?? VALUE="Visa">Visa<BR>
    ?? <INPUT TYPE="RADIO" NAME="cardType"
    ?? VALUE="Master Card">Master Card<BR>
    ?? <INPUT TYPE="RADIO" NAME="cardType"
    ?? VALUE="Amex">American Express<BR>
    ?? <INPUT TYPE="RADIO" NAME="cardType"
    ?? VALUE="Discover">Discover<BR>
    ?? <INPUT TYPE="RADIO" NAME="cardType"
    ?? VALUE="Java SmartCard">Java SmartCard<BR>
    ?? Credit Card Number:
    ?? <INPUT TYPE="PASSWORD" NAME="cardNum"><BR>
    ?? Repeat Credit Card Number:
    ?? <INPUT TYPE="PASSWORD" NAME="cardNum"><BR><BR>
    ?? <CENTER>
    ?? <INPUT TYPE="SUBMIT" VALUE="Submit Order">
    ?? </CENTER>
    ? </FORM>
    ??
    ? </BODY>
    ? </HTML>
    5.1 HTTP請(qǐng)求頭概述
    ??
    ?    HTTP客戶程序(例如瀏覽器),向服務(wù)器發(fā)送請(qǐng)求的時(shí)候必須指明請(qǐng)求類型(一般是GET或者POST)。如有必要,客戶程序還可以選擇發(fā)送其他的請(qǐng)求頭。大多數(shù)請(qǐng)求頭并不是必需的,但Content-Length除外。對(duì)于POST請(qǐng)求來說Content-Length必須出現(xiàn)。
    ??
    ?    下面是一些最常見的請(qǐng)求頭:
    ??
    ? Accept:瀏覽器可接受的MIME類型。
    ? Accept-Charset:瀏覽器可接受的字符集。
    ? Accept-Encoding:瀏覽器能夠進(jìn)行解碼的數(shù)據(jù)編碼方式,比如gzip。Servlet能夠向支持gzip的瀏覽器返回經(jīng)gzip編碼的HTML頁面。許多情形下這可以減少5到10倍的下載時(shí)間。
    ? Accept-Language:瀏覽器所希望的語言種類,當(dāng)服務(wù)器能夠提供一種以上的語言版本時(shí)要用到。
    ? Authorization:授權(quán)信息,通常出現(xiàn)在對(duì)服務(wù)器發(fā)送的WWW-Authenticate頭的應(yīng)答中。
    ? Connection:表示是否需要持久連接。如果Servlet看到這里的值為“Keep-Alive”,或者看到請(qǐng)求使用的是HTTP 1.1(HTTP 1.1默認(rèn)進(jìn)行持久連接),它就可以利用持久連接的優(yōu)點(diǎn),當(dāng)頁面包含多個(gè)元素時(shí)(例如Applet,圖片),顯著地減少下載所需要的時(shí)間。要實(shí)現(xiàn)這一點(diǎn),Servlet需要在應(yīng)答中發(fā)送一個(gè)Content-Length頭,最簡(jiǎn)單的實(shí)現(xiàn)方法是:先把內(nèi)容寫入ByteArrayOutputStream,然后在正式寫出內(nèi)容之前計(jì)算它的大小。
    ? Content-Length:表示請(qǐng)求消息正文的長(zhǎng)度。
    ? Cookie:這是最重要的請(qǐng)求頭信息之一,參見后面《Cookie處理》一章中的討論。
    ? From:請(qǐng)求發(fā)送者的email地址,由一些特殊的Web客戶程序使用,瀏覽器不會(huì)用到它。
    ? Host:初始URL中的主機(jī)和端口。
    ? If-Modified-Since:只有當(dāng)所請(qǐng)求的內(nèi)容在指定的日期之后又經(jīng)過修改才返回它,否則返回304“Not Modified”應(yīng)答。
    ? Pragma:指定“no-cache”值表示服務(wù)器必須返回一個(gè)刷新后的文檔,即使它是代理服務(wù)器而且已經(jīng)有了頁面的本地拷貝。
    ? Referer:包含一個(gè)URL,用戶從該URL代表的頁面出發(fā)訪問當(dāng)前請(qǐng)求的頁面。
    ? User-Agent:瀏覽器類型,如果Servlet返回的內(nèi)容與瀏覽器類型有關(guān)則該值非常有用。
    ? UA-Pixels,UA-Color,UA-OS,UA-CPU:由某些版本的IE瀏覽器所發(fā)送的非標(biāo)準(zhǔn)的請(qǐng)求頭,表示屏幕大小、顏色深度、操作系統(tǒng)和CPU類型。
    ?    有關(guān)HTTP頭完整、詳細(xì)的說明,請(qǐng)參見http://www.w3.org/Protocols/ 的HTTP規(guī)范。
    ??
    ?    5.2 在Servlet中讀取請(qǐng)求頭
    ??
    ?    在Servlet中讀取HTTP頭是非常方便的,只需要調(diào)用一下HttpServletRequest的getHeader方法即可。如果客戶請(qǐng)求中提供了指定的頭信息,getHeader返回對(duì)應(yīng)的字符串;否則,返回null。部分頭信息經(jīng)常要用到,它們有專用的訪問方法:getCookies方法返回Cookie頭的內(nèi)容,經(jīng)解析后存放在Cookie對(duì)象的數(shù)組中,請(qǐng)參見后面有關(guān)Cookie章節(jié)的討論;getAuthType和getRemoteUser方法分別讀取Authorization頭中的一部分內(nèi)容;getDateHeader和getIntHeader方法讀取指定的頭,然后返回日期值或整數(shù)值。
    ??
    ?    除了讀取指定的頭之外,利用getHeaderNames還可以得到請(qǐng)求中所有頭名字的一個(gè)Enumeration對(duì)象。
    ??
    ?    最后,除了查看請(qǐng)求頭信息之外,我們還可以從請(qǐng)求主命令行獲得一些信息。getMethod方法返回請(qǐng)求方法,請(qǐng)求方法通常是GET或者POST,但也有可能是HEAD、PUT或者DELETE。getRequestURI方法返回URI(URI是URL的從主機(jī)和端口之后到表單數(shù)據(jù)之前的那一部分)。getRequestProtocol返回請(qǐng)求命令的第三部分,一般是“HTTP/1.0”或者“HTTP/1.1”。
    ??
    ?    5.3 實(shí)例:輸出所有的請(qǐng)求頭
    ??
    ?    下面的Servlet實(shí)例把所有接收到的請(qǐng)求頭和它的值以表格的形式輸出。另外,該Servlet還會(huì)輸出主請(qǐng)求命令的三個(gè)部分:請(qǐng)求方法,URI,協(xié)議/版本。
    ??
    ?    ShowRequestHeaders.java
    ? package hall;
    ??
    ? import java.io.*;
    ? import javax.servlet.*;
    ? import javax.servlet.http.*;
    ? import java.util.*;
    ??
    ? public class ShowRequestHeaders extends HttpServlet {
    ?? public void doGet(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ?? response.setContentType("text/html");
    ?? PrintWriter out = response.getWriter();
    ?? String title = "顯示所有請(qǐng)求頭";
    ?? out.println(ServletUtilities.headWithTitle(title) +
    ?? "<BODY BGCOLOR=\"#FDF5E6\">\n" +
    ?? "<H1 ALIGN=CENTER>" + title + "</H1>\n" +
    ?? "<B>Request Method: </B>" +
    ?? request.getMethod() + "<BR>\n" +
    ?? "<B>Request URI: </B>" +
    ?? request.getRequestURI() + "<BR>\n" +
    ?? "<B>Request Protocol: </B>" +
    ?? request.getProtocol() + "<BR><BR>\n" +
    ?? "<TABLE BORDER=1 ALIGN=CENTER>\n" +
    ?? "<TR BGCOLOR=\"#FFAD00\">\n" +
    ?? "<TH>Header Name<TH>Header Value");
    ?? Enumeration headerNames = request.getHeaderNames();
    ?? while(headerNames.hasMoreElements()) {
    ?? String headerName = (String)headerNames.nextElement();
    ?? out.println("<TR><TD>" + headerName);
    ?? out.println(" <TD>" + request.getHeader(headerName));
    ?? }
    ?? out.println("</TABLE>\n</BODY></HTML>");
    ?? }
    ??
    ?? public void doPost(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ?? doGet(request, response);
    ?? }
    ? }
    6.1 CGI變量概述
    ??
    ?    如果你是從傳統(tǒng)的CGI編程轉(zhuǎn)而學(xué)習(xí)Java Servlet,或許已經(jīng)習(xí)慣了“CGI變量”這一概念。CGI變量匯集了各種有關(guān)請(qǐng)求的信息:
    ??
    ? 部分來自HTTP請(qǐng)求命令和請(qǐng)求頭,例如Content-Length頭;
    ? 部分來自Socket本身,例如主機(jī)的名字和IP地址;
    ? 也有部分與服務(wù)器安裝配置有關(guān),例如URL到實(shí)際路徑的映射。
    ?    6.2 標(biāo)準(zhǔn)CGI變量的Servlet等價(jià)表示
    ??
    ?    下表假定request對(duì)象是提供給doGet和doPost方法的HttpServletRequest類型對(duì)象。 CGI變量 含義 從doGet或doPost訪問
    ? AUTH_TYPE 如果提供了Authorization頭,這里指定了具體的模式(basic或者digest)。 request.getAuthType()
    ? CONTENT_LENGTH 只用于POST請(qǐng)求,表示所發(fā)送數(shù)據(jù)的字節(jié)數(shù)。 嚴(yán)格地講,等價(jià)的表達(dá)方式應(yīng)該是String.valueOf(request.getContentLength())(返回一個(gè)字符串)。但更常見的是用request.getContentLength()返回含義相同的整數(shù)。
    ? CONTENT_TYPE 如果指定的話,表示后面所跟數(shù)據(jù)的類型。 request.getContentType()
    ? DOCUMENT_ROOT 與http://host/對(duì)應(yīng)的路徑。 getServletContext().getRealPath("/")
    ? 注意低版本Servlet規(guī)范中的等價(jià)表達(dá)方式是request.getRealPath("/")。
    ??
    ? HTTP_XXX_YYY 訪問任意HTTP頭。 request.getHeader("Xxx-Yyy")
    ? PATH_INFO URL中的附加路徑信息,即URL中Servlet路徑之后、查詢字符串之前的那部分。 request.getPathInfo()
    ? PATH_TRANSLATED 映射到服務(wù)器實(shí)際路徑之后的路徑信息。 request.getPathTranslated()
    ? QUERY_STRING 這是字符串形式的附加到URL后面的查詢字符串,數(shù)據(jù)仍舊是URL編碼的。在Servlet中很少需要用到未經(jīng)解碼的數(shù)據(jù),一般使用getParameter訪問各個(gè)參數(shù)。 request.getQueryString()
    ? REMOTE_ADDR 發(fā)出請(qǐng)求的客戶機(jī)的IP地址。 request.getRemoteAddr()
    ? REMOTE_HOST 發(fā)出請(qǐng)求的客戶機(jī)的完整的域名,如java.sun.com。如果不能確定該域名,則返回IP地址。 request.getRemoteHost()
    ? REMOTE_USER 如果提供了Authorization頭,則代表其用戶部分。它代表發(fā)出請(qǐng)求的用戶的名字。 request.getRemoteUser()
    ? REQUEST_METHOD 請(qǐng)求類型。通常是GET或者POST。但偶爾也會(huì)出現(xiàn)HEAD,PUT, DELETE,OPTIONS,或者 TRACE. request.getMethod()
    ? SCRIPT_NAME URL中調(diào)用Servlet的那一部分,不包含附加路徑信息和查詢字符串。 request.getServletPath()
    ? SERVER_NAME Web服務(wù)器名字。 request.getServerName()
    ? SERVER_PORT 服務(wù)器監(jiān)聽的端口。 嚴(yán)格地說,等價(jià)表達(dá)應(yīng)該是返回字符串的String.valueOf(request.getServerPort())。但經(jīng)常使用返回整數(shù)值的request.getServerPort()。
    ? SERVER_PROTOCOL 請(qǐng)求命令中的協(xié)議名字和版本(即HTTP/1.0或HTTP/1.1)。 request.getProtocol()
    ? SERVER_SOFTWARE Servlet引擎的名字和版本。 getServletContext().getServerInfo()
    ??
    ??
    ?    6.3 實(shí)例:讀取CGI變量
    ??
    ?    下面這個(gè)Servlet創(chuàng)建一個(gè)表格,顯示除了HTTP_XXX_YYY之外的所有CGI變量。HTTP_XXX_YYY是HTTP請(qǐng)求頭信息,請(qǐng)參見上一節(jié)介紹。
    ??
    ?    ShowCGIVariables.java
    ? package hall;
    ??
    ? import java.io.*;
    ? import javax.servlet.*;
    ? import javax.servlet.http.*;
    ? import java.util.*;
    ??
    ? public class ShowCGIVariables extends HttpServlet {
    ?? public void doGet(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ?? response.setContentType("text/html");
    ?? PrintWriter out = response.getWriter();
    ?? String[][] variables =
    ?? { { "AUTH_TYPE", request.getAuthType() },
    ?? { "CONTENT_LENGTH", String.valueOf(request.getContentLength()) },
    ?? { "CONTENT_TYPE", request.getContentType() },
    ?? { "DOCUMENT_ROOT", getServletContext().getRealPath("/") },
    ?? { "PATH_INFO", request.getPathInfo() },
    ?? { "PATH_TRANSLATED", request.getPathTranslated() },
    ?? { "QUERY_STRING", request.getQueryString() },
    ?? { "REMOTE_ADDR", request.getRemoteAddr() },
    ?? { "REMOTE_HOST", request.getRemoteHost() },
    ?? { "REMOTE_USER", request.getRemoteUser() },
    ?? { "REQUEST_METHOD", request.getMethod() },
    ?? { "SCRIPT_NAME", request.getServletPath() },
    ?? { "SERVER_NAME", request.getServerName() },
    ?? { "SERVER_PORT", String.valueOf(request.getServerPort()) },
    ?? { "SERVER_PROTOCOL", request.getProtocol() },
    ?? { "SERVER_SOFTWARE", getServletContext().getServerInfo() }
    ?? };
    ?? String title = "顯示CGI變量";
    ?? out.println(ServletUtilities.headWithTitle(title) +
    ?? "<BODY BGCOLOR=\"#FDF5E6\">\n" +
    ?? "<H1 ALIGN=CENTER>" + title + "</H1>\n" +
    ?? "<TABLE BORDER=1 ALIGN=CENTER>\n" +
    ?? "<TR BGCOLOR=\"#FFAD00\">\n" +
    ?? "<TH>CGI Variable Name<TH>Value");
    ?? for(int i=0; i<variables.length; i++) {
    ?? String varName = variables[i][0];
    ?? String varValue = variables[i][1];
    ?? if (varValue == null)
    ?? varValue = "<I>Not specified</I>";
    ?? out.println("<TR><TD>" + varName + "<TD>" + varValue);
    ?? }
    ?? out.println("</TABLE></BODY></HTML>");
    ?? }
    ??
    ?? public void doPost(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ?? doGet(request, response);
    ?? }
    ? }
    7.1 狀態(tài)代碼概述
    ??
    ?    Web服務(wù)器響應(yīng)瀏覽器或其他客戶程序的請(qǐng)求時(shí),其應(yīng)答一般由以下幾個(gè)部分組成:一個(gè)狀態(tài)行,幾個(gè)應(yīng)答頭,一個(gè)空行,內(nèi)容文檔。下面是一個(gè)最簡(jiǎn)單的應(yīng)答:
    ? HTTP/1.1 200 OK
    ? Content-Type: text/plain
    ??
    ? Hello World
    ??
    ??
    ??
    ??
    ?    狀態(tài)行包含HTTP版本、狀態(tài)代碼、與狀態(tài)代碼對(duì)應(yīng)的簡(jiǎn)短說明信息。在大多數(shù)情況下,除了Content-Type之外的所有應(yīng)答頭都是可選的。但Content-Type是必需的,它描述的是后面文檔的MIME類型。雖然大多數(shù)應(yīng)答都包含一個(gè)文檔,但也有一些不包含,例如對(duì)HEAD請(qǐng)求的應(yīng)答永遠(yuǎn)不會(huì)附帶文檔。有許多狀態(tài)代碼實(shí)際上用來標(biāo)識(shí)一次失敗的請(qǐng)求,這些應(yīng)答也不包含文檔(或只包含一個(gè)簡(jiǎn)短的錯(cuò)誤信息說明)。
    ??
    ?    Servlet可以利用狀態(tài)代碼來實(shí)現(xiàn)許多功能。例如,可以把用戶重定向到另一個(gè)網(wǎng)站;可以指示出后面的文檔是圖片、PDF文件或HTML文件;可以告訴用戶必須提供密碼才能訪問文檔;等等。這一部分我們將具體討論各種狀態(tài)代碼的含義以及利用這些代碼可以做些什么。
    ??
    ?    7.2 設(shè)置狀態(tài)代碼
    ??
    ?    如前所述,HTTP應(yīng)答狀態(tài)行包含HTTP版本、狀態(tài)代碼和對(duì)應(yīng)的狀態(tài)信息。由于狀態(tài)信息直接和狀態(tài)代碼相關(guān),而HTTP版本又由服務(wù)器確定,因此需要Servlet設(shè)置的只有一個(gè)狀態(tài)代碼。
    ??
    ?    Servlet設(shè)置狀態(tài)代碼一般使用HttpServletResponse的setStatus方法。setStatus方法的參數(shù)是一個(gè)整數(shù)(即狀態(tài)代碼),不過為了使得代碼具有更好的可讀性,可以用HttpServletResponse中定義的常量來避免直接使用整數(shù)。這些常量根據(jù)HTTP 1.1中的標(biāo)準(zhǔn)狀態(tài)信息命名,所有的名字都加上了SC前綴(Status Code的縮寫)并大寫,同時(shí)把空格轉(zhuǎn)換成了下劃線。也就是說,與狀態(tài)代碼404對(duì)應(yīng)的狀態(tài)信息是“Not Found”,則HttpServletResponse中的對(duì)應(yīng)常量名字為SC_NOT_FOUND。但有兩個(gè)例外:和狀態(tài)代碼302對(duì)應(yīng)的常量根據(jù)HTTP 1.0命名,而307沒有對(duì)應(yīng)的常量。
    ??
    ?    設(shè)置狀態(tài)代碼并非總是意味著不要再返回文檔。例如,雖然大多數(shù)服務(wù)器返回404應(yīng)答時(shí)會(huì)輸出簡(jiǎn)單的“File Not Found”信息,但Servlet也可以定制這個(gè)應(yīng)答。不過,定制應(yīng)答時(shí)應(yīng)當(dāng)在通過PrintWriter發(fā)送任何內(nèi)容之前先調(diào)用response.setStatus。
    ??
    ?    雖然設(shè)置狀態(tài)代碼一般使用的是response.setStauts(int)方法,但為了簡(jiǎn)單起見,HttpServletResponse為兩種常見的情形提供了專用方法:sendError方法生成一個(gè)404應(yīng)答,同時(shí)生成一個(gè)簡(jiǎn)短的HTML錯(cuò)誤信息文檔;sendRedirect方法生成一個(gè)302應(yīng)答,同時(shí)在Location頭中指示新文檔的URL。
    ??
    ?    7.3 HTTP 1.1狀態(tài)代碼及其含義
    ??
    ?    下表顯示了常見的HTTP 1.1狀態(tài)代碼以及它們對(duì)應(yīng)的狀態(tài)信息和含義。
    ??
    ?    應(yīng)當(dāng)謹(jǐn)慎地使用那些只有HTTP 1.1支持的狀態(tài)代碼,因?yàn)樵S多瀏覽器還只能夠支持HTTP 1.0。如果你使用了HTTP 1.1特有的狀態(tài)代碼,最好能夠檢查一下請(qǐng)求的HTTP版本號(hào)(通過HttpServletRequest的getProtocol方法)。 狀態(tài)代碼 狀態(tài)信息 含義
    ? 100 Continue 初始的請(qǐng)求已經(jīng)接受,客戶應(yīng)當(dāng)繼續(xù)發(fā)送請(qǐng)求的其余部分。(HTTP 1.1新)
    ? 101 Switching Protocols 服務(wù)器將遵從客戶的請(qǐng)求轉(zhuǎn)換到另外一種協(xié)議(HTTP 1.1新)
    ? 200 OK 一切正常,對(duì)GET和POST請(qǐng)求的應(yīng)答文檔跟在后面。如果不用setStatus設(shè)置狀態(tài)代碼,Servlet默認(rèn)使用202狀態(tài)代碼。
    ? 201 Created 服務(wù)器已經(jīng)創(chuàng)建了文檔,Location頭給出了它的URL。
    ? 202 Accepted 已經(jīng)接受請(qǐng)求,但處理尚未完成。
    ? 203 Non-Authoritative Information 文檔已經(jīng)正常地返回,但一些應(yīng)答頭可能不正確,因?yàn)槭褂玫氖俏臋n的拷貝(HTTP 1.1新)。
    ? 204 No Content 沒有新文檔,瀏覽器應(yīng)該繼續(xù)顯示原來的文檔。如果用戶定期地刷新頁面,而Servlet可以確定用戶文檔足夠新,這個(gè)狀態(tài)代碼是很有用的。
    ? 205 Reset Content 沒有新的內(nèi)容,但瀏覽器應(yīng)該重置它所顯示的內(nèi)容。用來強(qiáng)制瀏覽器清除表單輸入內(nèi)容(HTTP 1.1新)。
    ? 206 Partial Content 客戶發(fā)送了一個(gè)帶有Range頭的GET請(qǐng)求,服務(wù)器完成了它(HTTP 1.1新)。
    ? 300 Multiple Choices 客戶請(qǐng)求的文檔可以在多個(gè)位置找到,這些位置已經(jīng)在返回的文檔內(nèi)列出。如果服務(wù)器要提出優(yōu)先選擇,則應(yīng)該在Location應(yīng)答頭指明。
    ? 301 Moved Permanently 客戶請(qǐng)求的文檔在其他地方,新的URL在Location頭中給出,瀏覽器應(yīng)該自動(dòng)地訪問新的URL。
    ? 302 Found 類似于301,但新的URL應(yīng)該被視為臨時(shí)性的替代,而不是永久性的。注意,在HTTP1.0中對(duì)應(yīng)的狀態(tài)信息是“Moved Temporatily”,而HttpServletResponse中相應(yīng)的常量是SC_MOVED_TEMPORARILY,而不是SC_FOUND。
    ? 出現(xiàn)該狀態(tài)代碼時(shí),瀏覽器能夠自動(dòng)訪問新的URL,因此它是一個(gè)很有用的狀態(tài)代碼。為此,Servlet提供了一個(gè)專用的方法,即sendRedirect。使用response.sendRedirect(url)比使用response.setStatus(response.SC_MOVED_TEMPORARILY)和response.setHeader("Location",url)更好。這是因?yàn)椋?
    ??
    ? 首先,代碼更加簡(jiǎn)潔。
    ? 第二,使用sendRedirect,Servlet會(huì)自動(dòng)構(gòu)造一個(gè)包含新鏈接的頁面(用于那些不能自動(dòng)重定向的老式瀏覽器)。
    ? 最后,sendRedirect能夠處理相對(duì)URL,自動(dòng)把它們轉(zhuǎn)換成絕對(duì)URL。
    ? 注意這個(gè)狀態(tài)代碼有時(shí)候可以和301替換使用。例如,如果瀏覽器錯(cuò)誤地請(qǐng)求http://host/~user(缺少了后面的斜杠),有的服務(wù)器返回301,有的則返回302。
    ??
    ? 嚴(yán)格地說,我們只能假定只有當(dāng)原來的請(qǐng)求是GET時(shí)瀏覽器才會(huì)自動(dòng)重定向。請(qǐng)參見307。
    ??
    ? 303 See Other 類似于301/302,不同之處在于,如果原來的請(qǐng)求是POST,Location頭指定的重定向目標(biāo)文檔應(yīng)該通過GET提?。℉TTP 1.1新)。
    ? 304 Not Modified 客戶端有緩沖的文檔并發(fā)出了一個(gè)條件性的請(qǐng)求(一般是提供If-Modified-Since頭表示客戶只想比指定日期更新的文檔)。服務(wù)器告訴客戶,原來緩沖的文檔還可以繼續(xù)使用。
    ? 305 Use Proxy 客戶請(qǐng)求的文檔應(yīng)該通過Location頭所指明的代理服務(wù)器提?。℉TTP 1.1新)。
    ? 307 Temporary Redirect 和302(Found)相同。許多瀏覽器會(huì)錯(cuò)誤地響應(yīng)302應(yīng)答進(jìn)行重定向,即使原來的請(qǐng)求是POST,即使它實(shí)際上只能在POST請(qǐng)求的應(yīng)答是303時(shí)才能重定向。由于這個(gè)原因,HTTP 1.1新增了307,以便更加清除地區(qū)分幾個(gè)狀態(tài)代碼:當(dāng)出現(xiàn)303應(yīng)答時(shí),瀏覽器可以跟隨重定向的GET和POST請(qǐng)求;如果是307應(yīng)答,則瀏覽器只能跟隨對(duì)GET請(qǐng)求的重定向。
    ? 注意,HttpServletResponse中沒有為該狀態(tài)代碼提供相應(yīng)的常量。(HTTP 1.1新)
    ??
    ? 400 Bad Request 請(qǐng)求出現(xiàn)語法錯(cuò)誤。
    ? 401 Unauthorized 客戶試圖未經(jīng)授權(quán)訪問受密碼保護(hù)的頁面。應(yīng)答中會(huì)包含一個(gè)WWW-Authenticate頭,瀏覽器據(jù)此顯示用戶名字/密碼對(duì)話框,然后在填寫合適的Authorization頭后再次發(fā)出請(qǐng)求。
    ? 403 Forbidden 資源不可用。服務(wù)器理解客戶的請(qǐng)求,但拒絕處理它。通常由于服務(wù)器上文件或目錄的權(quán)限設(shè)置導(dǎo)致。
    ? 404 Not Found 無法找到指定位置的資源。這也是一個(gè)常用的應(yīng)答,HttpServletResponse專門提供了相應(yīng)的方法:sendError(message)。
    ? 405 Method Not Allowed 請(qǐng)求方法(GET、POST、HEAD、DELETE、PUT、TRACE等)對(duì)指定的資源不適用。(HTTP 1.1新)
    ? 406 Not Acceptable 指定的資源已經(jīng)找到,但它的MIME類型和客戶在Accpet頭中所指定的不兼容(HTTP 1.1新)。
    ? 407 Proxy Authentication Required 類似于401,表示客戶必須先經(jīng)過代理服務(wù)器的授權(quán)。(HTTP 1.1新)
    ? 408 Request Timeout 在服務(wù)器許可的等待時(shí)間內(nèi),客戶一直沒有發(fā)出任何請(qǐng)求。客戶可以在以后重復(fù)同一請(qǐng)求。(HTTP 1.1新)
    ? 409 Conflict 通常和PUT請(qǐng)求有關(guān)。由于請(qǐng)求和資源的當(dāng)前狀態(tài)相沖突,因此請(qǐng)求不能成功。(HTTP 1.1新)
    ? 410 Gone 所請(qǐng)求的文檔已經(jīng)不再可用,而且服務(wù)器不知道應(yīng)該重定向到哪一個(gè)地址。它和404的不同在于,返回407表示文檔永久地離開了指定的位置,而404表示由于未知的原因文檔不可用。(HTTP 1.1新)
    ? 411 Length Required 服務(wù)器不能處理請(qǐng)求,除非客戶發(fā)送一個(gè)Content-Length頭。(HTTP 1.1新)
    ? 412 Precondition Failed 請(qǐng)求頭中指定的一些前提條件失敗(HTTP 1.1新)。
    ? 413 Request Entity Too Large 目標(biāo)文檔的大小超過服務(wù)器當(dāng)前愿意處理的大小。如果服務(wù)器認(rèn)為自己能夠稍后再處理該請(qǐng)求,則應(yīng)該提供一個(gè)Retry-After頭(HTTP 1.1新)。
    ? 414 Request URI Too Long URI太長(zhǎng)(HTTP 1.1新)。
    ? 416 Requested Range Not Satisfiable 服務(wù)器不能滿足客戶在請(qǐng)求中指定的Range頭。(HTTP 1.1新)
    ? 500 Internal Server Error 服務(wù)器遇到了意料不到的情況,不能完成客戶的請(qǐng)求。
    ? 501 Not Implemented 服務(wù)器不支持實(shí)現(xiàn)請(qǐng)求所需要的功能。例如,客戶發(fā)出了一個(gè)服務(wù)器不支持的PUT請(qǐng)求。
    ? 502 Bad Gateway 服務(wù)器作為網(wǎng)關(guān)或者代理時(shí),為了完成請(qǐng)求訪問下一個(gè)服務(wù)器,但該服務(wù)器返回了非法的應(yīng)答。
    ? 503 Service Unavailable 服務(wù)器由于維護(hù)或者負(fù)載過重未能應(yīng)答。例如,Servlet可能在數(shù)據(jù)庫連接池已滿的情況下返回503。服務(wù)器返回503時(shí)可以提供一個(gè)Retry-After頭。
    ? 504 Gateway Timeout 由作為代理或網(wǎng)關(guān)的服務(wù)器使用,表示不能及時(shí)地從遠(yuǎn)程服務(wù)器獲得應(yīng)答。(HTTP 1.1新)
    ? 505 HTTP Version Not Supported 服務(wù)器不支持請(qǐng)求中所指明的HTTP版本。(HTTP 1.1新)
    ??
    ??
    ?    7.4 實(shí)例:訪問多個(gè)搜索引擎
    ??
    ?    下面這個(gè)例子用到了除200之外的另外兩個(gè)常見狀態(tài)代碼:302和404。302通過sendRedirect方法設(shè)置,404通過sendError方法設(shè)置。
    ??
    ?    在這個(gè)例子中,首先出現(xiàn)的HTML表單用來選擇搜索引擎、搜索字符串、每頁顯示的搜索結(jié)果數(shù)量。表單提交后,Servlet提取這三個(gè)變量,按照所選擇的搜索引擎的要求構(gòu)造出包含這些變量的URL,然后把用戶重定向到這個(gè)URL。如果用戶不能正確地選擇搜索引擎,或者利用其他表單發(fā)送了一個(gè)不認(rèn)識(shí)的搜索引擎名字,則返回一個(gè)提示搜索引擎找不到的404頁面。
    ??
    ?    SearchEngines.java
    ??
    ?    注意:這個(gè)Servlet要用到后面給出的SearchSpec類,SearchSpec的功能是構(gòu)造適合不同搜索引擎的URL。
    ? package hall;
    ??
    ? import java.io.*;
    ? import javax.servlet.*;
    ? import javax.servlet.http.*;
    ? import java.net.*;
    ??
    ? public class SearchEngines extends HttpServlet {
    ?? public void doGet(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ?? // getParameter自動(dòng)解碼URL編碼的查詢字符串。由于我們
    ?? // 要把查詢字符串發(fā)送給另一個(gè)服務(wù)器,因此再次使用
    ?? // URLEncoder進(jìn)行URL編碼
    ?? String searchString =
    ?? URLEncoder.encode(request.getParameter("searchString"));
    ?? String numResults =
    ?? request.getParameter("numResults");
    ?? String searchEngine =
    ?? request.getParameter("searchEngine");
    ?? SearchSpec[] commonSpecs = SearchSpec.getCommonSpecs();
    ?? for(int i=0; i<commonSpecs.length; i++) {
    ?? SearchSpec searchSpec = commonSpecs[i];
    ?? if (searchSpec.getName().equals(searchEngine)) {
    ?? String url =
    ?? response.encodeURL(searchSpec.makeURL(searchString,
    ?? numResults));
    ?? response.sendRedirect(url);
    ?? return;
    ?? }
    ?? }
    ?? response.sendError(response.SC_NOT_FOUND,
    ?? "No recognized search engine specified.");
    ?? }
    ??
    ?? public void doPost(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ?? doGet(request, response);
    ?? }
    ? }
    ??
    ??
    ??
    ??
    ?    SearchSpec.java
    ? package hall;
    ??
    ? class SearchSpec {
    ?? private String name, baseURL, numResultsSuffix;
    ??
    ?? private static SearchSpec[] commonSpecs =
    ?? { new SearchSpec("google",
    ?? "http://www.google.com/search?q=",
    ?? "&num="),
    ?? new SearchSpec("infoseek",
    ?? "http://infoseek.go.com/Titles?qt=",
    ?? "&nh="),
    ?? new SearchSpec("lycos",
    ?? "http://lycospro.lycos.com/cgi-bin/pursuit?query=",
    ?? "&maxhits="),
    ?? new SearchSpec("hotbot",
    ?? "http://www.hotbot.com/?MT=",
    ?? "&DC=")
    ?? };
    ??
    ?? public SearchSpec(String name,
    ?? String baseURL,
    ?? String numResultsSuffix) {
    ?? this.name = name;
    ?? this.baseURL = baseURL;
    ?? this.numResultsSuffix = numResultsSuffix;
    ?? }
    ??
    ?? public String makeURL(String searchString, String numResults) {
    ?? return(baseURL + searchString + numResultsSuffix + numResults);
    ?? }
    ??
    ?? public String getName() {
    ?? return(name);
    ?? }
    ??
    ?? public static SearchSpec[] getCommonSpecs() {
    ?? return(commonSpecs);
    ?? }
    ? }
    ??
    ??
    ??
    ??
    ?    SearchEngines.html
    ??
    ?    下面是調(diào)用上述Servlet的HTML表單。
    ? <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
    ? <HTML>
    ? <HEAD>
    ?? <TITLE>訪問多個(gè)搜索引擎</TITLE>
    ? </HEAD>
    ??
    ? <BODY BGCOLOR="#FDF5E6">
    ??
    ? <FORM ACTION="/servlet/hall.SearchEngines">
    ?? <CENTER>
    ?? 搜索關(guān)鍵字:
    ?? <INPUT TYPE="TEXT" NAME="searchString"><BR>
    ?? 每頁顯示幾個(gè)查詢結(jié)果:
    ?? <INPUT TYPE="TEXT" NAME="numResults"
    ?? VALUE=10 SIZE=3><BR>
    ?? <INPUT TYPE="RADIO" NAME="searchEngine"
    ?? VALUE="google">
    ?? Google |
    ?? <INPUT TYPE="RADIO" NAME="searchEngine"
    ?? VALUE="infoseek">
    ?? Infoseek |
    ?? <INPUT TYPE="RADIO" NAME="searchEngine"
    ?? VALUE="lycos">
    ?? Lycos |
    ?? <INPUT TYPE="RADIO" NAME="searchEngine"
    ?? VALUE="hotbot">
    ?? HotBot
    ?? <BR>
    ?? <INPUT TYPE="SUBMIT" VALUE="Search">
    ?? </CENTER>
    ? </FORM>
    ??
    ? </BODY>
    ? </HTML>
    8.1 HTTP應(yīng)答頭概述
    ??
    ?    Web服務(wù)器的HTTP應(yīng)答一般由以下幾項(xiàng)構(gòu)成:一個(gè)狀態(tài)行,一個(gè)或多個(gè)應(yīng)答頭,一個(gè)空行,內(nèi)容文檔。設(shè)置HTTP應(yīng)答頭往往和設(shè)置狀態(tài)行中的狀態(tài)代碼結(jié)合起來。例如,有好幾個(gè)表示“文檔位置已經(jīng)改變”的狀態(tài)代碼都伴隨著一個(gè)Location頭,而401(Unauthorized)狀態(tài)代碼則必須伴隨一個(gè)WWW-Authenticate頭。
    ??
    ?    然而,即使在沒有設(shè)置特殊含義的狀態(tài)代碼時(shí),指定應(yīng)答頭也是很有用的。應(yīng)答頭可以用來完成:設(shè)置Cookie,指定修改日期,指示瀏覽器按照指定的間隔刷新頁面,聲明文檔的長(zhǎng)度以便利用持久HTTP連接,……等等許多其他任務(wù)。
    ??
    ?    設(shè)置應(yīng)答頭最常用的方法是HttpServletResponse的setHeader,該方法有兩個(gè)參數(shù),分別表示應(yīng)答頭的名字和值。和設(shè)置狀態(tài)代碼相似,設(shè)置應(yīng)答頭應(yīng)該在發(fā)送任何文檔內(nèi)容之前進(jìn)行。
    ??
    ?    setDateHeader方法和setIntHeadr方法專門用來設(shè)置包含日期和整數(shù)值的應(yīng)答頭,前者避免了把Java時(shí)間轉(zhuǎn)換為GMT時(shí)間字符串的麻煩,后者則避免了把整數(shù)轉(zhuǎn)換為字符串的麻煩。
    ??
    ?    HttpServletResponse還提供了許多設(shè)置常見應(yīng)答頭的簡(jiǎn)便方法,如下所示:
    ??
    ? setContentType:設(shè)置Content-Type頭。大多數(shù)Servlet都要用到這個(gè)方法。
    ? setContentLength:設(shè)置Content-Length頭。對(duì)于支持持久HTTP連接的瀏覽器來說,這個(gè)函數(shù)是很有用的。
    ? addCookie:設(shè)置一個(gè)Cookie(Servlet API中沒有setCookie方法,因?yàn)閼?yīng)答往往包含多個(gè)Set-Cookie頭)。
    ? 另外,如上節(jié)介紹,sendRedirect方法設(shè)置狀態(tài)代碼302時(shí)也會(huì)設(shè)置Location頭。
    ?    8.2 常見應(yīng)答頭及其含義
    ??
    ?    有關(guān)HTTP頭詳細(xì)和完整的說明,請(qǐng)參見http://www.w3.org/Protocols/ 規(guī)范。
    ??
    ? 應(yīng)答頭 說明
    ? Allow 服務(wù)器支持哪些請(qǐng)求方法(如GET、POST等)。
    ? Content-Encoding 文檔的編碼(Encode)方法。只有在解碼之后才可以得到Content-Type頭指定的內(nèi)容類型。利用gzip壓縮文檔能夠顯著地減少HTML文檔的下載時(shí)間。Java的GZIPOutputStream可以很方便地進(jìn)行g(shù)zip壓縮,但只有Unix上的Netscape和Windows上的IE 4、IE 5才支持它。因此,Servlet應(yīng)該通過查看Accept-Encoding頭(即request.getHeader("Accept-Encoding"))檢查瀏覽器是否支持gzip,為支持gzip的瀏覽器返回經(jīng)gzip壓縮的HTML頁面,為其他瀏覽器返回普通頁面。
    ? Content-Length 表示內(nèi)容長(zhǎng)度。只有當(dāng)瀏覽器使用持久HTTP連接時(shí)才需要這個(gè)數(shù)據(jù)。如果你想要利用持久連接的優(yōu)勢(shì),可以把輸出文檔寫入ByteArrayOutputStram,完成后查看其大小,然后把該值放入Content-Length頭,最后通過byteArrayStream.writeTo(response.getOutputStream()發(fā)送內(nèi)容。
    ? Content-Type 表示后面的文檔屬于什么MIME類型。Servlet默認(rèn)為text/plain,但通常需要顯式地指定為text/html。由于經(jīng)常要設(shè)置Content-Type,因此HttpServletResponse提供了一個(gè)專用的方法setContentTyep。
    ? Date 當(dāng)前的GMT時(shí)間。你可以用setDateHeader來設(shè)置這個(gè)頭以避免轉(zhuǎn)換時(shí)間格式的麻煩。
    ? Expires 應(yīng)該在什么時(shí)候認(rèn)為文檔已經(jīng)過期,從而不再緩存它?
    ? Last-Modified 文檔的最后改動(dòng)時(shí)間??蛻艨梢酝ㄟ^If-Modified-Since請(qǐng)求頭提供一個(gè)日期,該請(qǐng)求將被視為一個(gè)條件GET,只有改動(dòng)時(shí)間遲于指定時(shí)間的文檔才會(huì)返回,否則返回一個(gè)304(Not Modified)狀態(tài)。Last-Modified也可用setDateHeader方法來設(shè)置。
    ? Location 表示客戶應(yīng)當(dāng)?shù)侥睦锶ヌ崛∥臋n。Location通常不是直接設(shè)置的,而是通過HttpServletResponse的sendRedirect方法,該方法同時(shí)設(shè)置狀態(tài)代碼為302。
    ? Refresh 表示瀏覽器應(yīng)該在多少時(shí)間之后刷新文檔,以秒計(jì)。除了刷新當(dāng)前文檔之外,你還可以通過setHeader("Refresh", "5; URL=http://host/path")讓瀏覽器讀取指定的頁面。
    ? 注意這種功能通常是通過設(shè)置HTML頁面HEAD區(qū)的<META HTTP-EQUIV="Refresh" CONTENT="5;URL=http://host/path">實(shí)現(xiàn),這是因?yàn)?,自?dòng)刷新或重定向?qū)τ谀切┎荒苁褂肅GI或Servlet的HTML編寫者十分重要。但是,對(duì)于Servlet來說,直接設(shè)置Refresh頭更加方便。
    ??
    ? 注意Refresh的意義是“N秒之后刷新本頁面或訪問指定頁面”,而不是“每隔N秒刷新本頁面或訪問指定頁面”。因此,連續(xù)刷新要求每次都發(fā)送一個(gè)Refresh頭,而發(fā)送204狀態(tài)代碼則可以阻止瀏覽器繼續(xù)刷新,不管是使用Refresh頭還是<META HTTP-EQUIV="Refresh" ...>。
    ??
    ? 注意Refresh頭不屬于HTTP 1.1正式規(guī)范的一部分,而是一個(gè)擴(kuò)展,但Netscape和IE都支持它。
    ??
    ? Server 服務(wù)器名字。Servlet一般不設(shè)置這個(gè)值,而是由Web服務(wù)器自己設(shè)置。
    ? Set-Cookie 設(shè)置和頁面關(guān)聯(lián)的Cookie。Servlet不應(yīng)使用response.setHeader("Set-Cookie", ...),而是應(yīng)使用HttpServletResponse提供的專用方法addCookie。參見下文有關(guān)Cookie設(shè)置的討論。
    ? WWW-Authenticate 客戶應(yīng)該在Authorization頭中提供什么類型的授權(quán)信息?在包含401(Unauthorized)狀態(tài)行的應(yīng)答中這個(gè)頭是必需的。例如,response.setHeader("WWW-Authenticate", "BASIC realm=\"executives\"")。
    ? 注意Servlet一般不進(jìn)行這方面的處理,而是讓W(xué)eb服務(wù)器的專門機(jī)制來控制受密碼保護(hù)頁面的訪問(例如.htaccess)。
    ??
    ??
    ??
    ?    8.3 實(shí)例:內(nèi)容改變時(shí)自動(dòng)刷新頁面
    ??
    ?    下面這個(gè)Servlet用來計(jì)算大素?cái)?shù)。因?yàn)橛?jì)算非常大的數(shù)字(例如500位)可能要花不少時(shí)間,所以Servlet將立即返回已經(jīng)找到的結(jié)果,同時(shí)在后臺(tái)繼續(xù)計(jì)算。后臺(tái)計(jì)算使用一個(gè)優(yōu)先級(jí)較低的線程以避免過多地影響Web服務(wù)器的性能。如果計(jì)算還沒有完成,Servlet通過發(fā)送Refresh頭指示瀏覽器在幾秒之后繼續(xù)請(qǐng)求新的內(nèi)容。
    ??
    ?    注意,本例除了說明HTTP應(yīng)答頭的用處之外,還顯示了Servlet的另外兩個(gè)很有價(jià)值的功能。首先,它表明Servlet能夠處理多個(gè)并發(fā)的連接,每個(gè)都有自己的線程。Servlet維護(hù)了一份已有素?cái)?shù)計(jì)算請(qǐng)求的Vector表,通過查找素?cái)?shù)個(gè)數(shù)(素?cái)?shù)列表的長(zhǎng)度)和數(shù)字個(gè)數(shù)(每個(gè)素?cái)?shù)的長(zhǎng)度)將當(dāng)前請(qǐng)求和已有請(qǐng)求相匹配,把所有這些請(qǐng)求同步到這個(gè)列表上。第二,本例證明,在Servlet中維持請(qǐng)求之間的狀態(tài)信息是非常容易的。維持狀態(tài)信息在傳統(tǒng)的CGI編程中是一件很麻煩的事情。由于維持了狀態(tài)信息,瀏覽器能夠在刷新頁面時(shí)訪問到正在進(jìn)行的計(jì)算過程,同時(shí)也使得Servlet能夠保存一個(gè)有關(guān)最近請(qǐng)求結(jié)果的列表,當(dāng)一個(gè)新的請(qǐng)求指定了和最近請(qǐng)求相同的參數(shù)時(shí)可以立即返回結(jié)果。
    ??
    ?    PrimeNumbers.java
    ??
    ?    注意,該Servlet要用到前面給出的ServletUtilities.java。另外還要用到:PrimeList.java,用于在后臺(tái)線程中創(chuàng)建一個(gè)素?cái)?shù)的Vector;Primes.java,用于隨機(jī)生成BigInteger類型的大數(shù)字,檢查它們是否是素?cái)?shù)。(此處略去PrimeList.java和Primes.java的代碼。)
    ? package hall;
    ??
    ? import java.io.*;
    ? import javax.servlet.*;
    ? import javax.servlet.http.*;
    ? import java.util.*;
    ??
    ? public class PrimeNumbers extends HttpServlet {
    ?? private static Vector primeListVector = new Vector();
    ?? private static int maxPrimeLists = 30;
    ??
    ?? public void doGet(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ?? int numPrimes = ServletUtilities.getIntParameter(request, "numPrimes", 50);
    ?? int numDigits = ServletUtilities.getIntParameter(request, "numDigits", 120);
    ?? PrimeList primeList = findPrimeList(primeListVector, numPrimes, numDigits);
    ?? if (primeList == null) {
    ?? primeList = new PrimeList(numPrimes, numDigits, true);
    ?? synchronized(primeListVector) {
    ?? if (primeListVector.size() >= maxPrimeLists)
    ?? primeListVector.removeElementAt(0);
    ?? primeListVector.addElement(primeList);
    ?? }
    ?? }
    ?? Vector currentPrimes = primeList.getPrimes();
    ?? int numCurrentPrimes = currentPrimes.size();
    ?? int numPrimesRemaining = (numPrimes - numCurrentPrimes);
    ?? boolean isLastResult = (numPrimesRemaining == 0);
    ?? if (!isLastResult) {
    ?? response.setHeader("Refresh", "5");
    ?? }
    ?? response.setContentType("text/html");
    ?? PrintWriter out = response.getWriter();
    ?? String title = "Some " + numDigits + "-Digit Prime Numbers";
    ?? out.println(ServletUtilities.headWithTitle(title) +
    ?? "<BODY BGCOLOR=\"#FDF5E6\">\n" +
    ?? "<H2 ALIGN=CENTER>" + title + "</H2>\n" +
    ?? "<H3>Primes found with " + numDigits +
    ?? " or more digits: " + numCurrentPrimes + ".</H3>");
    ?? if (isLastResult)
    ?? out.println("<B>Done searching.</B>");
    ?? else
    ?? out.println("<B>Still looking for " + numPrimesRemaining +
    ?? " more<BLINK>...</BLINK></B>");
    ?? out.println("<OL>");
    ?? for(int i=0; i<numCurrentPrimes; i++) {
    ?? out.println(" <LI>" + currentPrimes.elementAt(i));
    ?? }
    ?? out.println("</OL>");
    ?? out.println("</BODY></HTML>");
    ?? }
    ??
    ?? public void doPost(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ?? doGet(request, response);
    ?? }
    ??
    ?? // 檢查是否存在同類型請(qǐng)求(已經(jīng)完成,或者正在計(jì)算)。
    ?? // 如存在,則返回現(xiàn)有結(jié)果而不是啟動(dòng)新的后臺(tái)線程。
    ?? private PrimeList findPrimeList(Vector primeListVector,
    ?? int numPrimes,
    ?? int numDigits) {
    ?? synchronized(primeListVector) {
    ?? for(int i=0; i<primeListVector.size(); i++) {
    ?? PrimeList primes = (PrimeList)primeListVector.elementAt(i);
    ?? if ((numPrimes == primes.numPrimes()) &&
    ?? (numDigits == primes.numDigits()))
    ?? return(primes);
    ?? }
    ?? return(null);
    ?? }
    ?? }
    ? }?


    posted on 2006-09-01 17:20 阿成 閱讀(617) 評(píng)論(0)  編輯  收藏 所屬分類: JSP&SERVLET
    主站蜘蛛池模板: 无人在线观看免费高清| 国产精品免费在线播放| 精品久久久久亚洲| a级毛片免费网站| 男女作爱在线播放免费网站| 1区2区3区产品乱码免费| 无码中文字幕av免费放| 国产精品无码免费视频二三区| 亚洲成片观看四虎永久| 亚洲国产精品嫩草影院在线观看| 亚洲视频免费在线播放| 色天使色婷婷在线影院亚洲| 好吊色永久免费视频大全 | 亚洲youjizz| 在线精品自拍亚洲第一区| 本道天堂成在人线av无码免费| 99视频在线免费| 女人让男人免费桶爽30分钟| 亚洲美日韩Av中文字幕无码久久久妻妇| 亚洲精品午夜无码电影网| 亚洲二区在线视频| 日韩在线观看视频免费| 91人成网站色www免费下载| 成人一a毛片免费视频| 亚洲中文久久精品无码ww16| 亚洲人成黄网在线观看| 新最免费影视大全在线播放| 麻豆国产精品免费视频| 亚洲国产成人乱码精品女人久久久不卡 | 免费观看亚洲人成网站| 久久精品夜色国产亚洲av| 亚洲妇女无套内射精| 久久99免费视频| 精品国产免费一区二区| 亚洲AV综合色区无码一区| 亚洲av成人一区二区三区观看在线| 最近中文字幕免费大全| 最近中文字幕mv手机免费高清| 亚洲色欲色欲www在线丝| 亚洲精品无码久久久久秋霞| 久久青草免费91观看|