Servlet - 速查指南



Servlet - 概覽

什麼是 Servlet?

Java Servlet 是在 Web 或應用程式伺服器上執行的程式,充當來自 Web 瀏覽器或其他 HTTP 客戶端的請求與 HTTP 伺服器上的資料庫或應用程式之間的中間層。

使用 Servlet,您可以透過網頁表單收集使用者輸入,顯示來自資料庫或其他來源的記錄並動態建立網頁。

Java Servlet 通常與使用通用閘道器介面 (CGI) 實現的程式具有相同用途。但是,與 CGI 相比,Servlet 具有多項優勢。

  • 效能大幅提升。

  • Servlet 在 Web 伺服器的地址空間內執行。不必為處理每個客戶端請求都建立單獨的程序。

  • Servlet 由於是用 Java 編寫的,因此與平臺無關。

  • 伺服器上的 Java 安全管理器強制執行一組限制以保護伺服器計算機上的資源。因此,Servlet 是可信的。

  • Servlet 可以使用 Java 類庫的全部功能。它可以透過已經瞭解的套接字和 RMI 機制與 applet、資料庫或其他軟體進行通訊。

Servlet 架構

下圖顯示了 Servlet 在 Web 應用程式中的位置。

Servlets Architecture

Servlet 任務

Servlet 執行下列主要任務 −

  • 讀取客戶端(瀏覽器)傳送的顯式資料。這包括網頁上的 HTML 表單,也可以來自 applet 或自定義 HTTP 客戶端程式。

  • 讀取客戶端(瀏覽器)傳送的隱式 HTTP 請求資料。這包括 Cookie、瀏覽器理解的媒體型別和壓縮方案等。

  • 處理資料並生成結果。此過程可能需要與資料庫通訊、執行 RMI 或 CORBA 呼叫、呼叫 Web 服務或直接計算響應。

  • 向客戶端(瀏覽器)傳送顯式資料(即文件)。此文件可以多種格式傳送,包括文字(HTML 或 XML)、二進位制(GIF 影像)、Excel 等。

  • 向客戶端(瀏覽器)傳送隱式 HTTP 響應。這包括告知瀏覽器或其他客戶端正在返回何種型別的文件(例如 HTML)、設定 Cookie 和快取引數以及其他此類任務。

Servlet 軟體包

Servlet 是 Java 類,由具有支援 Java Servlet 規範的直譯器的 Web 伺服器執行。

使用javax.servletjavax.servlet.http包可以建立 Servlet,它們是 Java 企業版的標準部分,後者是支援大規模開發專案的 Java 類庫的擴充套件版本。

這些類實現 Java Servlet 和 JSP 規範。在編寫本教程時,Java Servlet 為 2.5 版,JSP 為 2.1 版。

Java servlet 的建立和編譯與任何其他 Java 類一樣。安裝 servlet 包並將它們新增到計算機的類路徑後,可以使用 JDK 的 Java 編譯器或任何其他當前編譯器編譯 servlet。

接下來是什麼?

我將一步步帶你設定環境,開始學習 Servlet。因此,繫好安全帶,體驗 servlet 之旅。我確信你會非常喜歡本教程。

Servlet - 環境設定

開發環境是你開發、測試並最終執行 Servlet 的環境。

與任何其他 Java 程式一樣,你需要使用 Java 編譯器javac編譯 servlet,在編譯 servlet 應用程式後,將其部署在配置好的環境中以進行測試和執行。

此開發環境設定涉及以下步驟 -

設定 Java 開發工具包

此步驟涉及下載 Java 軟體開發工具包 (SDK) 的實現並相應設定 PATH 環境變數。

你可以從 Oracle 的 Java 站點下載 SDK - Java SE 下載

下載 Java 實現後,按照給定的說明進行安裝和配置設定。最後設定 PATH 和 JAVA_HOME 環境變數,使其分別引用包含 java 和 javac 的目錄,通常為 java_install_dir/bin 和 java_install_dir。

如果你執行的是 Windows,並將 SDK 安裝在 C:\jdk1.8.0_65 中,則可以在 C:\autoexec.bat 檔案中新增以下行。

set PATH = C:\jdk1.8.0_65\bin;%PATH% 
set JAVA_HOME = C:\jdk1.8.0_65 

或者,在 Windows NT/2000/XP 中,你還可以在“我的電腦”上單擊滑鼠右鍵,選擇“屬性”,然後選擇“高階”和“環境變數”。然後,你需要更新 PATH 值並按“確定”按鈕。

在 Unix(Solaris、Linux 等)中,如果 SDK 安裝在 /usr/local/jdk1.8.0_65 中,而你使用 C shell,則需要將以下內容放入到 .cshrc 檔案中。

setenv PATH /usr/local/jdk1.8.0_65/bin:$PATH 
setenv JAVA_HOME /usr/local/jdk1.8.0_65

或者,如果你使用 Borland JBuilder、Eclipse、IntelliJ IDEA 或 Sun ONE Studio 等整合開發環境 (IDE),請編譯並執行一個簡單程式以確認 IDE 知道你安裝 Java 的位置。

設定 Web 伺服器 - Tomcat

市場上有很多支援 servlet 的 Web 伺服器。有些 Web 伺服器可以免費下載,而 Tomcat 就是其中之一。

Apache Tomcat 是 Java Servlet 和 Java Server Pages 技術的開源軟體實現,可以使用它作為單獨的伺服器來測試 servlet,也可以將其與 Apache Web 伺服器整合。以下是設定你的計算機上 Tomcat 的步驟 -

  • https://tomcat.apache.org/ 下載最新版本的 Tomcat。

  • 下載安裝程式後,將二進位制發行版解壓縮到方便的位置。例如,在 Windows 中的 C:\apache-tomcat-8.0.28,或者在 Linux/Unix 中的 /usr/local/apache-tomcat-8.0.289,並建立指向這些位置的 CATALINA_HOME 環境變數。

透過在 Windows 計算機上執行以下命令可以啟動 Tomcat -

%CATALINA_HOME%\bin\startup.bat
or
C:\apache-tomcat-8.0.28\bin\startup.bat

透過在 Unix(Solaris、Linux 等)計算機上執行以下命令可以啟動 Tomcat -

$CATALINA_HOME/bin/startup.sh
or
/usr/local/apache-tomcat-8.0.28/bin/startup.sh

啟動後,可以透過訪問https://:8080/來使用 Tomcat 附帶的預設 Web 應用程式。如果一切正常,則應顯示以下結果 -

Tomcat Home page

有關配置和執行 Tomcat 的更多資訊,請參閱此處包含的文件以及 Tomcat 網站 - http://tomcat.apache.org

Tomcat 可透過在 Windows 電腦上執行以下命令停止 −

C:\apache-tomcat-8.0.28\bin\shutdown 

Tomcat 可透過在 Unix(Solaris、Linux 等)電腦上執行以下命令停止 −

/usr/local/apache-tomcat-8.0.28/bin/shutdown.sh

設定 CLASSPATH

由於 servlet 不屬於 Java Platform, Standard Edition,因此你必須向編譯器指定 servlet 類。

如果你執行 Windows,你需要將以下行新增到你的 C:\autoexec.bat 檔案中。

set CATALINA = C:\apache-tomcat-8.0.28 
set CLASSPATH = %CATALINA%\common\lib\servlet-api.jar;%CLASSPATH% 

或者,在 Windows NT/2000/XP 上,你可以到“我的電腦” −>“屬性” −>“高階” −>“環境變數”。然後,更新 CLASSPATH 值並按下“確定”按鈕。

在 Unix(Solaris、Linux 等)上,如果你使用 C shell,你會將以下行新增到你的 .cshrc 檔案中。

setenv CATALINA = /usr/local/apache-tomcat-8.0.28
setenv CLASSPATH $CATALINA/common/lib/servlet-api.jar:$CLASSPATH

− 假設你的開發目錄為 C:\ServletDevel(Windows)或 /usr/ServletDevel(Unix),那麼你需要在 CLASSPATH 中新增這些目錄,方式與你在上面新增的一樣。

Servlet - 生命週期

servlet 生命週期可以定義為從其建立到銷燬的整個過程。以下是 servlet 遵循的路徑。

  • servlet 透過呼叫 init() 方法初始化。

  • servlet 呼叫 service() 方法處理來自客戶端的請求。

  • servlet 透過呼叫 destroy() 方法終止。

  • 最後,servlet 由 JVM 的垃圾回收器進行垃圾回收。

現在讓我們詳細討論一下生命週期方法。

init() 方法

init 方法只調用一次。它只在 servlet 建立時呼叫,之後不對任何使用者請求呼叫。所以它用於一次性初始化,就像 applet 的 init 方法一樣。

當用戶第一次呼叫對應於 servlet 的 URL 時,servlet 通常會被建立,但你也可以指定在伺服器第一次啟動時載入 servlet。

當用戶呼叫 servlet 時,每個 servlet 的單個例項都會被建立,每個使用者請求都會產生一個新執行緒,交由 doGet 或 doPost(視情況而定)。init() 方法只會建立或載入一些將在 servlet 整個生命週期中使用的資料。

init 方法的定義如下 −

public void init() throws ServletException {
   // Initialization code...
}

service() 方法

service() 方法是執行實際任務的主要方法。servlet 容器(即網路伺服器)呼叫 service() 方法來處理來自客戶端(瀏覽器)的請求並向客戶端寫回格式化的響應。

每次伺服器接收到對 servlet 的請求時,伺服器都會生成一個新執行緒並呼叫 service。service() 方法檢查 HTTP 請求型別(GET、POST、PUT、DELETE 等),並相應地呼叫 doGet、doPost、doPut、doDelete 等方法。

此方法的簽名如下 −

public void service(ServletRequest request, ServletResponse response) 
   throws ServletException, IOException {
}

service() 方法由容器呼叫,service 方法呼叫 doGet、doPost、doPut、doDelete 等方法(視情況而定)。所以你無需處理 service() 方法,而根據你從客戶端收到的請求型別覆蓋 doGet() 或 doPost() 中的一個。

doGet() 和 doPost() 是在每個 service 請求中使用最頻繁的方法。以下是這兩個方法的簽名。

doGet() 方法

GET 請求源自對 URL 的正常請求或未指定 METHOD 的 HTML 表單,應由 doGet() 方法處理。

public void doGet(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {
   // Servlet code
}

doPost() 方法

POST 請求源自明確將 POST 列為 METHOD 的 HTML 表單,應由 doPost() 方法處理。

public void doPost(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {
   // Servlet code
}

destroy() 方法

destroy() 方法只在 servlet 生命週期結束時呼叫一次。此方法為你的 servlet 提供關閉資料庫連線、停止後臺執行緒、將 cookie 列表或點選計數寫入磁碟以及執行其他此類清理活動的機會。

在呼叫 destroy() 方法後,servlet 物件被標記為垃圾回收。destroy 方法的定義如下 −

public void destroy() {
   // Finalization code...
}

架構圖

下圖描述了典型的 Servlet 生命週期場景。

  • 首先,發往伺服器的 HTTP 請求會被委託給 Servlet 容器。

  • 在呼叫 service() 方法之前,Servlet 容器會載入 Servlet。

  • 然後,Servlet 容器透過衍生多個執行緒(每個執行緒執行 Servlet 的單個例項的 service() 方法)來處理多個請求。

Servlet Life Cycle

Servlet - 例項

Servlet 是 Java 類,可服務於 HTTP 請求並實現 **javax.servlet.Servlet** 介面。Web 應用程式開發人員通常編寫的是擴充套件自 **javax.servlet.http.HttpServlet** 的 Servlet,javax.servlet.http.HttpServlet 是一個實現 Servlet 介面,且經過專門設計,用於處理 HTTP 請求的抽象類。

示例程式碼

以下是顯示 Hello World 的 Servlet 示例的示例原始碼結構 −

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

// Extend HttpServlet class
public class HelloWorld extends HttpServlet {
 
   private String message;

   public void init() throws ServletException {
      // Do required initialization
      message = "Hello World";
   }

   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // Set response content type
      response.setContentType("text/html");

      // Actual logic goes here.
      PrintWriter out = response.getWriter();
      out.println("<h1>" + message + "</h1>");
   }

   public void destroy() {
      // do nothing.
   }
}

編譯 Servlet

讓我們建立一個名為 HelloWorld.java 的檔案,其中包含上面所示的程式碼。將此檔案放置在 C:\ServletDevel(在 Windows 中)或 /usr/ServletDevel(在 Unix 中)。在繼續進行之前,必須將此路徑位置新增到 CLASSPATH。

假設您的環境設定正確,請轉到 **ServletDevel** 目錄並按照以下指令編譯 HelloWorld.java −

$ javac HelloWorld.java

如果 Servlet 依賴於任何其他庫,您還必須將這些 JAR 檔案包含在您的 CLASSPATH 中。我僅包含了 servlet-api.jar JAR 檔案,因為我在 Hello World 程式中不使用任何其他庫。

此命令列使用 Sun Microsystems Java 軟體開發包 (JDK) 附帶的內建 javac 編譯器。為了使此命令正常工作,您必須在 PATH 環境變數中包含您正在使用的 Java SDK 的位置。

如果一切順利,上面的編譯將在同一目錄中生成 **HelloWorld.class** 類檔案。下一部分將說明如何將已編譯的 Servlet 部署到生產環境中。

Servlet 部署

預設情況下,Servlet 應用程式位於路徑 <Tomcat-installationdirectory>/webapps/ROOT,類檔案將駐留在 <Tomcat-installationdirectory>/webapps/ROOT/WEB-INF/classes 中。

如果您擁有 **com.myorg.MyServlet** 的完全限定類名,則此 servlet 類必須位於 WEB-INF/classes/com/myorg/MyServlet.class 中。

現在,讓我們將 HelloWorld.class 複製到 <Tomcat-installationdirectory>/webapps/ROOT/WEB-INF/classes,並在位於 <Tomcat-installation-directory>/webapps/ROOT/WEB-INF/ 中的 **web.xml** 檔案中建立以下條目

<servlet>
   <servlet-name>HelloWorld</servlet-name>
   <servlet-class>HelloWorld</servlet-class>
</servlet>

<servlet-mapping>
   <servlet-name>HelloWorld</servlet-name>
   <url-pattern>/HelloWorld</url-pattern>
</servlet-mapping>

要建立在 web.xml 檔案中可用的 <web-app>...</web-app> 標記內部的上述條目。此表中可能已提供各種條目,但不必理會。

您幾乎已完成,現在讓我們使用 <Tomcat-installationdirectory>\bin\startup.bat(在 Windows 中)或 <Tomcat-installationdirectory>/bin/startup.sh(在 Linux/Solaris 等中)啟動 tomcat 伺服器,最後在瀏覽器地址框中鍵入 **https://:8080/HelloWorld**。如果一切順利,您將獲得以下結果

Servlet Example

Servlet - 表單資料

您一定遇到過需要將某些資訊從您的瀏覽器傳遞到網路伺服器並最終傳遞到您的後端程式的情況。瀏覽器使用兩種方法將此資訊傳遞到網路伺服器。這些方法是 GET 方法和 POST 方法。

GET 方法

GET 方法將編碼的使用者資訊附加在頁面請求後面傳送。頁面和編碼資訊由 **?**(問號)符號分隔,如下所示 −

http://www.test.com/hello?key1 = value1&key2 = value2

GET 方法是將資訊從瀏覽器傳遞到 Web 伺服器的預設方法,它會產生一個很長的字串,此字串會出現在瀏覽器的位置:框中。如果您有密碼或其他敏感資訊要傳遞到伺服器,則切勿使用 GET 方法。GET 方法有大小限制:請求字串中只能使用 1024 個字元。

此資訊使用 QUERY_STRING 標頭傳遞,並且可以透過 QUERY_STRING 環境變數訪問,而 Servlet 使用 doGet() 方法處理此類請求。

POST 方法

將資訊傳遞到後端程式時,通常更可靠的方法是 POST 方法。這種方法打包資訊的方式與 GET 方法完全相同,但它不是在 URL 中的 ?(問號)後面將其作為文字字串傳送,而是將其作為單獨的訊息傳送。此訊息以標準輸入的形式傳送到後端程式,您可以對其進行解析並將其用於處理。Servlet 使用 doPost() 方法處理此類請求。

使用 Servlet 讀取表單資料

根據情況,Servlet 使用以下方法自動處理表單資料解析 −

  • getParameter() − 呼叫 request.getParameter() 方法以獲取表單引數的值。

  • getParameterValues() − 如果引數出現多次,則呼叫此方法並返回多個值(例如選擇框)。

  • getParameterNames() − 如果您想要當前請求中所有引數的完整列表,請呼叫此方法。

使用 URL 的 GET 方法示例

下面是一個簡單的 URL,它將使用 GET 方法向 HelloForm 程式傳遞兩個值。

https://:8080/HelloForm?first_name = ZARA&last_name = ALI

下面是處理 Web 瀏覽器提供輸入的 HelloForm.java Servlet 程式。我們將使用 getParameter() 方法,該方法可非常輕鬆地訪問傳遞的資訊 −

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

// Extend HttpServlet class
public class HelloForm extends HttpServlet {
 
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // Set response content type
      response.setContentType("text/html");

      PrintWriter out = response.getWriter();
      String title = "Using GET Method to Read Form Data";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n";
         
      out.println(docType +
         "<html>\n" +
            "<head><title>" + title + "</title></head>\n" +
            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1 align = \"center\">" + title + "</h1>\n" +
               "<ul>\n" +
                  "  <li><b>First Name</b>: "
                  + request.getParameter("first_name") + "\n" +
                  "  <li><b>Last Name</b>: "
                  + request.getParameter("last_name") + "\n" +
               "</ul>\n" +
            "</body>" +
         "</html>"
      );
   }
}

假設您的環境已正確設定,請按如下方式編譯 HelloForm.java −

$ javac HelloForm.java

如果一切正常,則上述編譯將生成 HelloForm.class 檔案。接下來,您必須將此類檔案複製到 <Tomcat 安裝目錄>/webapps/ROOT/WEB-INF/classes 中,並在 <Tomcat 安裝目錄>/webapps/ROOT/WEB-INF/ 中找到的 web.xml 檔案中建立以下條目:

<servlet>
   <servlet-name>HelloForm</servlet-name>
   <servlet-class>HelloForm</servlet-class>
</servlet>

<servlet-mapping>
   <servlet-name>HelloForm</servlet-name>
   <url-pattern>/HelloForm</url-pattern>
</servlet-mapping>

現在在瀏覽器的 Location:box 中鍵入 https://:8080/HelloForm?first_name=ZARA&last_name=ALI,並且在瀏覽器中執行上述命令之前,請確保您已經啟動了 Tomcat 伺服器。這將生成以下結果 −

Using GET Method to Read Form Data

  • First Name: ZARA
  • Last Name: ALI

使用表單的 GET 方法示例

下面是一個簡單的示例,它使用 HTML FORM 和提交按鈕傳遞兩個值。我們將使用相同的 HelloForm Servlet 來處理此輸入。

<html>
   <body>
      <form action = "HelloForm" method = "GET">
         First Name: <input type = "text" name = "first_name">
         <br />
         Last Name: <input type = "text" name = "last_name" />
         <input type = "submit" value = "Submit" />
      </form>
   </body>
</html>

將此 HTML 儲存在 Hello.htm 檔案中並將其放在 <Tomcat 安裝目錄>/webapps/ROOT 目錄中。當您訪問 https://:8080/Hello.htm 時,以下是上述表單的實際輸出。

姓氏

嘗試輸入名字和姓氏,然後單擊提交按鈕,在本地計算機上檢視結果(執行 Tomcat 的計算機)。根據提供的輸入,它將生成與上述示例中提到的類似結果。

使用表單的 POST 方法示例

讓我們對上述 Servlet 進行一些修改,以便它可以處理 GET 和 POST 方法。下面是使用 GET 或 POST 方法處理 Web 瀏覽器提供的輸入的 HelloForm.java Servlet 程式。

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

// Extend HttpServlet class
public class HelloForm extends HttpServlet {

   // Method to handle GET method request.
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // Set response content type
      response.setContentType("text/html");

      PrintWriter out = response.getWriter();
      String title = "Using GET Method to Read Form Data";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " +
         "transitional//en\">\n";
         
      out.println(docType +
         "<html>\n" +
            "<head><title>" + title + "</title></head>\n" +
            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1 align = \"center\">" + title + "</h1>\n" +
               "<ul>\n" +
                  "  <li><b>First Name</b>: "
                  + request.getParameter("first_name") + "\n" +
                  "  <li><b>Last Name</b>: "
                  + request.getParameter("last_name") + "\n" +
               "</ul>\n" +
            "</body>"
         "</html>"
      );
   }

   // Method to handle POST method request.
   public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

      doGet(request, response);
   }
}

現在編譯並部署上述 Servlet,然後使用以下 POST 方法和 Hello.htm 對其進行測試 −

<html>
   <body>
      <form action = "HelloForm" method = "POST">
         First Name: <input type = "text" name = "first_name">
         <br />
         Last Name: <input type = "text" name = "last_name" />
         <input type = "submit" value = "Submit" />
      </form>
   </body>
</html>

以下是上述表單的實際輸出。嘗試輸入名字和姓氏,然後單擊提交按鈕,在本地計算機上檢視結果(執行 Tomcat 的計算機)。

姓氏

根據提供的輸入,它將生成與上述示例中提到的類似結果。

向 Servlet 程式傳遞選擇框資料

如果需要選擇多個選項,則使用選擇框。

下面是具有兩個選擇框的表單的 HTML 程式碼示例 CheckBox.htm

<html>
   <body>
      <form action = "CheckBox" method = "POST" target = "_blank">
         <input type = "checkbox" name = "maths" checked = "checked" /> Maths
         <input type = "checkbox" name = "physics"  /> Physics
         <input type = "checkbox" name = "chemistry" checked = "checked" /> 
                                          Chemistry
         <input type = "submit" value = "Select Subject" />
      </form>
   </body>
</html>

以下程式碼會生成以下形式的結果

數學物理化學

以下是用於處置 Web 瀏覽器中為複選框按鈕提供的輸入的 CheckBox.java servlet 程式。

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

// Extend HttpServlet class
public class CheckBox extends HttpServlet {
 
   // Method to handle GET method request.
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // Set response content type
      response.setContentType("text/html");

      PrintWriter out = response.getWriter();
      String title = "Reading Checkbox Data";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n";

      out.println(docType +
         "<html>\n" +
            "<head><title>" + title + "</title></head>\n" +
            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1 align = \"center\">" + title + "</h1>\n" +
               "<ul>\n" +
                  "  <li><b>Maths Flag : </b>: "
                  + request.getParameter("maths") + "\n" +
                  "  <li><b>Physics Flag: </b>: "
                  + request.getParameter("physics") + "\n" +
                  "  <li><b>Chemistry Flag: </b>: "
                  + request.getParameter("chemistry") + "\n" +
               "</ul>\n" +
            "</body>"
         "</html>"
      );
   }

   // Method to handle POST method request.
   public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      doGet(request, response);
   }
}

對於上述示例,它將顯示以下結果 −

Reading Checkbox Data

  • Maths Flag : : on
  • Physics Flag: : null
  • Chemistry Flag: : on

讀取全部表單引數

以下是使用 HttpServletRequest 的 getParameterNames() 方法讀取所有可用表單引數的通用示例。此方法會返回一個列舉,其中包含以未指定順序排列的引數名稱

一旦我們擁有了一個列舉,我們就可以使用 hasMoreElements() 方法以標準方式向下迴圈列舉,以確定何時停止,並使用 nextElement() 方法獲取每個引數名稱。

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

// Extend HttpServlet class
public class ReadParams extends HttpServlet {
 
   // Method to handle GET method request.
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // Set response content type
      response.setContentType("text/html");

      PrintWriter out = response.getWriter();
      String title = "Reading All Form Parameters";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n";

      out.println(docType +
         "<html>\n" +
         "<head><title>" + title + "</title></head>\n" +
         "<body bgcolor = \"#f0f0f0\">\n" +
         "<h1 align = \"center\">" + title + "</h1>\n" +
         "<table width = \"100%\" border = \"1\" align = \"center\">\n" +
         "<tr bgcolor = \"#949494\">\n" +
            "<th>Param Name</th>"
            "<th>Param Value(s)</th>\n"+
         "</tr>\n"
      );

      Enumeration paramNames = request.getParameterNames();

      while(paramNames.hasMoreElements()) {
         String paramName = (String)paramNames.nextElement();
         out.print("<tr><td>" + paramName + "</td>\n<td>");
         String[] paramValues = request.getParameterValues(paramName);

         // Read single valued data
         if (paramValues.length == 1) {
            String paramValue = paramValues[0];
            if (paramValue.length() == 0)
               out.println("<i>No Value</i>");
               else
               out.println(paramValue);
         } else {
            // Read multiple valued data
            out.println("<ul>");

            for(int i = 0; i < paramValues.length; i++) {
               out.println("<li>" + paramValues[i]);
            }
            out.println("</ul>");
         }
      }
      out.println("</tr>\n</table>\n</body></html>");
   }
   
   // Method to handle POST method request.
   public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      doGet(request, response);
   }
}

現在,針對以下表單嘗試上述 servlet −

<html>
   <body>
      <form action = "ReadParams" method = "POST" target = "_blank">
         <input type = "checkbox" name = "maths" checked = "checked" /> Maths
         <input type = "checkbox" name = "physics"  /> Physics
         <input type = "checkbox" name = "chemistry" checked = "checked" /> Chem
         <input type = "submit" value = "Select Subject" />
      </form>
   </body>
</html>

現在,使用上述表單呼叫 servlet 會生成以下結果 −

Reading All Form Parameters

Param Name Param Value(s)
maths on
chemistry on

你可以嘗試上述 servlet 以讀取任何其他包含其他物件(如文字框、單選按鈕或下拉選單等)的資料的表單。

Servlet - 客戶機 HTTP 請求

當瀏覽器請求一個網頁時,它將傳送大量資訊給 Web 伺服器,這些資訊無法直接讀取,因為這些資訊的傳輸是 HTTP 請求報頭的一部分。你可以檢視 HTTP 協議 以瞭解更多相關資訊。

以下是從瀏覽器端獲取的重要頭部資訊,並且在 Web 程式設計中非常常用 −

編號 頭部和描述
1

Accept

此頭部指定瀏覽器或其他客戶機可以處理的 MIME 型別。image/pngimage/jpeg 的值為兩個最常見的可能性。

2

Accept-Charset

此頭部指定瀏覽器可以用於顯示資訊的字元集。例如,ISO-8859-1。

3

Accept-Encoding

此頭部指定瀏覽器知道如何處理的編碼的型別。gzipcompress 的值為兩個最常見的可能性。

4

Accept-Language

如果 servlet 可以以多種語言提供結果,則此頭部指定客戶機首選的語言。例如,en、en-us、ru 等

5

Authorization

客戶機在訪問受密碼保護的網頁時可以使用此頭部來識別自己。

6

Connection

此頭部指示客戶機是否可以處理持續的 HTTP 連線。持續的連線允許客戶機或其他瀏覽器使用單個請求檢索多個檔案。Keep-Alive 的值表示應使用持續的連線。

7

Content-Length

此頭部只適用於 POST 請求,並以位元組為單位提供 POST 資料的大小。

8

Cookie

此頭部將 cookie 返還給先前將它們傳送到瀏覽器的伺服器。

9

Host

此頭部指定原始 URL 中所述的主機和埠。

10

If-Modified-Since

此頭部指示只有在指定日期後頁面有所更改時客戶機才需要該頁面。如果未獲得較新的結果,則伺服器會發送 code,304 表示 未修改 的頭部。

11

If-Unmodified-Since

此頭部是 If-Modified-Since 的反向,它指定的操作僅在文件早於指定日期時才應成功。

12

Referer

此頭部指示引用網頁的 URL。例如,如果你在網頁 1 上並點選指向網頁 2 的連結時,當瀏覽器請求網頁 2 時,網頁 1 的 URL 將包含在 Referer 頭部中。

13

User-Agent

此頭部標識發起請求的瀏覽器或其他客戶機,並可用於將不同的內容返還給不同型別的瀏覽器。

讀取 HTTP 頭部的使用方法

可在 servlet 程式中使用以下方法來讀取 HTTP 頭部。這些方法可透過 HttpServletRequest 物件獲得

編號 方法和描述
1

Cookie[] getCookies()

返回客戶端隨此請求傳送的所有 Cookie 物件包含的陣列。

2

列舉 getAttributeNames()

返回一個列舉,內含此請求可用的屬性名稱。

3

列舉 getHeaderNames()

返回此請求包含的所有頭名稱的列舉。

4

列舉 getParameterNames()

返回 String 物件的列舉,內含此請求中包含的引數的名稱。

5

HttpSession getSession()

返回與此請求關聯的當前會話,或在請求沒有會話時,建立一個會話。

6

HttpSession getSession(boolean create)

返回與此請求關聯的當前 HttpSession 或在沒有當前會話且 create 的值為 true 時,返回一個新會話。

7

Locale getLocale()

根據 Accept-Language 頭,返回客戶端將接受何種首選語言的內容。

8

Object getAttribute(String name)

返回命名屬性的值,如果給定名稱的屬性不存在,則返回 null。

9

ServletInputStream getInputStream()

使用一個 ServletInputStream 以二進位制資料形式檢索請求主體。

10

String getAuthType()

返回用於保護 servlet 的身份驗證方案的名稱(例如“BASIC”或“SSL”),或在 JSP 未受到保護時返回 null。

11

String getCharacterEncoding()

返回此請求主體中使用的字元編碼的名稱。

12

String getContentType()

返回請求主體的 MIME 型別,或在型別未知時返回 null。

13

String getContextPath()

返回請求 URI 的指示請求上下文的這一部分。

14

String getHeader(String name)

以 String 的形式返回指定請求頭的值。

15

String getMethod()

返回用於提出此請求的 HTTP 方法的名稱,例如,GET、POST 或 PUT。

16

String getParameter(String name)

以 String 的形式返回請求引數的值,或在引數不存在時返回 null。

17

String getPathInfo()

返回客戶端在提出此請求時隨 URL 傳送的任何額外路徑資訊。

18

String getProtocol()

返回協議的名稱和版本。

19

String getQueryString()

返回路徑後的請求 URL 中包含的查詢字串。

20

String getRemoteAddr()

返回傳送請求客戶端的網際網路協議 (IP) 地址。

21

String getRemoteHost()

返回傳送請求客戶端的完全限定名。

22

String getRemoteUser()

如果使用者已透過身份驗證,則返回提出此請求的使用者的登入,或在使用者未透過身份驗證時返回 null。

23

String getRequestURI()

返回此請求的 URL 中從協議名稱到 HTTP 請求第一行中查詢字串的部分。

24

String getRequestedSessionId()

返回客戶端指定的會話 ID。

25

String getServletPath()

返回此請求的 URL 中呼叫 JSP 的部分。

26

String[] getParameterValues(String name)

返回 String 物件的陣列,內含給定請求引數所有的值,或在引數不存在時返回 null。

27

boolean isSecure()

返回一個布林值,指示此請求是否透過安全隧道(例如 HTTPS)發出。

28

int getContentLength()

返回請求主體的長度(以位元組為單位),由輸入流提供,或在長度未知時返回 -1。

29

int getIntHeader(String name)

以 int 的形式返回指定請求頭的值。

30

int getServerPort()

返回接收此請求的埠號。

HTTP 標頭請求示例

下面是一個示例,它使用 getHeaderNames() 方法從 HttpServletRequest 中讀取 HTTP 標頭資訊。該方法返回包含與當前 HTTP 請求關聯的標頭資訊的列舉。

一旦獲得列舉,我們就可以使用 hasMoreElements() 方法確定何時停止,並使用 nextElement() 方法獲取每個引數名稱,按照標準的方式迴圈列舉

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
 
// Extend HttpServlet class
public class DisplayHeader extends HttpServlet {
 
   // Method to handle GET method request.
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // Set response content type
      response.setContentType("text/html");
 
      PrintWriter out = response.getWriter();
      String title = "HTTP Header Request Example";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n";

      out.println(docType +
         "<html>\n" +
         "<head><title>" + title + "</title></head>\n"+
         "<body bgcolor = \"#f0f0f0\">\n" +
         "<h1 align = \"center\">" + title + "</h1>\n" +
         "<table width = \"100%\" border = \"1\" align = \"center\">\n" +
         "<tr bgcolor = \"#949494\">\n" +
         "<th>Header Name</th><th>Header Value(s)</th>\n"+
         "</tr>\n"
      );
 
      Enumeration headerNames = request.getHeaderNames();
    
      while(headerNames.hasMoreElements()) {
         String paramName = (String)headerNames.nextElement();
         out.print("<tr><td>" + paramName + "</td>\n");
         String paramValue = request.getHeader(paramName);
         out.println("<td> " + paramValue + "</td></tr>\n");
      }
      out.println("</table>\n</body></html>");
   }
   
   // Method to handle POST method request.
   public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

      doGet(request, response);
   }
}

現在,呼叫以上 servlet 將生成以下結果 -

HTTP Header Request Example

Header Name Header Value(s)
accept */*
accept-language en-us
user-agent Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; InfoPath.2; MS-RTC LM 8)
accept-encoding gzip, deflate
host localhost:8080
connection Keep-Alive
cache-control no-cache

Servlet - 伺服器 HTTP 響應

如前一章所述,當 Web 伺服器響應 HTTP 請求時,響應通常由狀態行、一些響應標頭、空行和文件組成。典型的響應如下所示 -

HTTP/1.1 200 OK
Content-Type: text/html
Header2: ...
...
HeaderN: ...
   (Blank Line)
<!doctype ...>
<html>
   <head>...</head>
   <body>
      ...
   </body>
</html>

該狀態行包含 HTTP 版本(示例中為 HTTP/1.1)、一個狀態程式碼(示例中為 200)和與狀態程式碼(示例中為 OK)對應的一個很短的訊息。

以下是 Web 伺服器最常用的 HTTP 1.1 響應標頭,您將在 Web 程式設計中使用它們 -

編號 頭部和描述
1

Allow

該標頭指定伺服器支援的請求方法 (GET、POST 等)。

2

Cache-Control

該標頭指定響應文件可以安全快取的情況下。它的值可以為 publicprivatenocache 等。public 意味著文件可快取,private 意味著文件是為單個使用者提供的,並且只能儲存在私有(非共享)快取中,nocache 意味著文件永遠不應快取。

3

Connection

該標頭指導瀏覽器是否在 HTTP 連線中使用持久連線。close 值指瀏覽器不使用持久 HTTP 連線,而 keepalive 意味著使用持久連線。

4

Content-Disposition

該標頭允許您請求瀏覽器要求使用者將響應另存為指定名稱的檔案到磁碟。

5

Content-Encoding

該標頭指定在傳輸期間編碼頁面方式。

6

Content-Language

該標頭表示文件的書寫語言。例如 en、en-us、ru 等

7

Content-Length

該標頭指示響應中的位元組數。只有當瀏覽器使用持久(keep-alive)HTTP 連線時才需要該資訊。

8

Content-Type

該標頭提供響應文件的 MIME(多用途網際網路郵件擴充套件)型別。

9

Expires

該標頭指定內容應被視為過時的時間,因此不再快取。

10

Last-Modified

該標頭指示文件最後一次更改的時間。然後,客戶端可以快取文件,並在以後的請求中透過 If-Modified-Since 請求標頭提供日期。

11

Location

該標頭應包含在所有狀態程式碼為 300 的響應中。這會將文件地址通知瀏覽器。瀏覽器會自動重新連線到該位置並檢索新文件。

12

Refresh

該標頭指定瀏覽器多久請求更新的頁面。您可以指定以秒為單位的時間,頁面將在該時間後重新整理。

13

Retry-After

該標頭可以與 503(服務不可用)響應結合使用,以告訴客戶端多久可以重複其請求。

14

Set-Cookie

該標頭指定與頁面關聯的 cookie。

設定 HTTP 響應標頭的方法

以下是可用於在您的 servlet 程式中設定 HTTP 響應標頭的方法。這些方法可透過 HttpServletResponse 物件獲得。

編號 方法和描述
1

String encodeRedirectURL(String url)

對指定的 URL 進行編碼以用於 sendRedirect 方法,或如果不需要編碼,則返回未更改的 URL。

2

String encodeURL(String url)

對指定的 URL 進行編碼,方法是包含其中的會話 ID,或如果不需要編碼,則返回未更改的 URL。

3

boolean containsHeader(String name)

返回布林值,表明所命名的響應標頭是否已被設定。

4

boolean isCommitted()

返回布林值,表明響應是否被提交。

5

void addCookie(Cookie cookie)

將指定的 cookie 新增到響應中。

6

void addDateHeader(String name, long date)

使用給定的名稱和日期值新增響應標頭。

7

void addHeader(String name, String value)

使用給定的名稱和值新增響應標頭。

8

void addIntHeader(String name, int value)

使用給定的名稱和整數值新增響應標頭。

9

void flushBuffer()

強制緩衝區中的任何內容寫到客戶端。

10

void reset()

清除快取在任何資料,以及狀態程式碼和標頭。

11

void resetBuffer()

清除響應中底層緩衝區的內容,而不清除標頭或狀態程式碼。

12

void sendError(int sc)

使用指定的錯誤程式碼向客戶端傳送錯誤響應,並清除緩衝區。

13

void sendError(int sc, String msg)

使用指定的錯誤向客戶端傳送錯誤響應

14

void sendRedirect(String location)

使用指定的重定向位置 URL 向客戶端傳送臨時重定向響應。

15

void setBufferSize(int size)

設定響應正文的首選緩衝區大小。

16

void setCharacterEncoding(String charset)

設定傳送到客戶端的響應的字元編碼(MIME 字元集),例如 UTF-8。

17

void setContentLength(int len)

設定響應中內容正文的長度。在 HTTP servlet 中,此方法設定 HTTP Content-Length 標頭。

18

void setContentType(String type)

設定傳送到客戶端的響應的內容型別(如果響應尚未提交)。

19

void setDateHeader(String name, long date)

使用給定的名稱和日期值設定響應標頭。

20

void setHeader(String name, String value)

使用給定的名稱和值設定響應標頭。

21

void setIntHeader(String name, int value)

使用給定的名稱和整數值設定響應標頭

22

void setLocale(Locale loc)

設定響應的語言環境(如果響應尚未提交)。

23

void setStatus(int sc)

設定該響應的狀態程式碼

HTTP 標頭響應示例

已經在前面的示例中看到 setContentType() 方法的作用,下面的示例也將會使用同樣的方法,此外還會使用 setIntHeader() 方法來設定 Refresh 標頭。

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
 
// Extend HttpServlet class
public class Refresh extends HttpServlet {
 
   // Method to handle GET method request.
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
   
      // Set refresh, autoload time as 5 seconds
      response.setIntHeader("Refresh", 5);
 
      // Set response content type
      response.setContentType("text/html");
 
      // Get current time
      Calendar calendar = new GregorianCalendar();
      String am_pm;
      int hour = calendar.get(Calendar.HOUR);
      int minute = calendar.get(Calendar.MINUTE);
      int second = calendar.get(Calendar.SECOND);
         
      if(calendar.get(Calendar.AM_PM) == 0)
         am_pm = "AM";
      else
         am_pm = "PM";
 
      String CT = hour+":"+ minute +":"+ second +" "+ am_pm;
    
      PrintWriter out = response.getWriter();
      String title = "Auto Refresh Header Setting";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n";

      out.println(docType +
         "<html>\n" +
         "<head><title>" + title + "</title></head>\n"+
         "<body bgcolor = \"#f0f0f0\">\n" +
         "<h1 align = \"center\">" + title + "</h1>\n" +
         "<p>Current Time is: " + CT + "</p>\n"
      );
   }
   
   // Method to handle POST method request.
   public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      doGet(request, response);
   }
}

現在呼叫上面的 servlet 將每隔 5 秒顯示一次當前系統時間,如下所示。只需執行 servlet 並等待檢視結果 -

Auto Refresh Header Setting

Current Time is: 9:44:50 PM

Servlet - HTTP 狀態程式碼

HTTP 請求和 HTTP 響應訊息的格式相似,並且具有以下結構 -

  • 一個初始狀態行 + CRLF(回車 + 換行符,即換行)

  • 零個或多個標頭行 + CRLF

  • 一個空行,即 CRLF

  • 一個可選的訊息正文,如檔案、查詢資料或查詢輸出。

例如,伺服器響應標頭如下所示 -

HTTP/1.1 200 OK
Content-Type: text/html
Header2: ...
...
HeaderN: ...
   (Blank Line)
<!doctype ...>
<html>
   <head>...</head>
   <body>
      ...
   </body>
</html>

該狀態行包含 HTTP 版本(示例中為 HTTP/1.1)、一個狀態程式碼(示例中為 200)和與狀態程式碼(示例中為 OK)對應的一個很短的訊息。

下面列出了從 Web 伺服器返回的 HTTP 狀態程式碼和相關訊息 -

程式碼 訊息 描述
100 繼續 伺服器只接收到了請求的一部分,但只要它沒有被拒絕,客戶端就應該繼續進行請求
101 切換協議 伺服器切換協議。
200 確定 請求是正確的
201 已建立 請求完成,已建立新資源
202 已被接受 已接受處理請求,但處理尚未完成。
203 非授權資訊  
204 無內容  
205 重置內容  
206 部分內容  
300 多項選擇 連結列表。使用者可以選擇一個連結並轉到該位置。最多五個地址
301 永久移動 請求的頁面已移至新網址
302 已找到 請求的頁面已暫時移至新網址
303 請檢視其他內容 請求的頁面可以在不同的網址下找到
304 未修改  
305 使用代理  
306 未使用 此程式碼曾在以前版本中使用過。不再使用,但程式碼已保留
307 臨時重定向 請求的頁面已暫時移動到新網址。
400 請求錯誤 伺服器未能理解請求
401 未授權 請求的頁面需要使用者名稱和密碼
402 需要付款 您目前無法使用此程式碼
403 禁止 禁止訪問請求的頁面
404 未找到 伺服器找不到請求的頁面。
405 方法不允許 請求中指定的方法不允許。
406 不可接受 伺服器只能生成客戶端無法接受的響應。
407 需要代理身份驗證 在提供此請求之前,您必須使用代理伺服器進行身份驗證。
408 請求超時 請求花費的時間超出了伺服器準備等待的時間。
409 衝突 由於衝突,請求無法完成。
410 已消失 請求的頁面不再可用。
411 需要長度 未定義“Content-Length”。如果沒有此專案,伺服器將不接受請求。
412 前提失敗 請求中給出的前提條件由伺服器評估為假。
413 請求實體過大 伺服器將不接受請求,因為請求實體過大。
414 請求 URL 過長 伺服器將不接受請求,因為 URL 過長。當您將“post”請求轉換為具有長查詢資訊的“get”請求時,會出現此情況。
415 不支援的媒體型別 伺服器將不接受請求,因為媒體型別不受支援。
417 期望失敗  
500 內部伺服器錯誤 請求未完成。伺服器遇到意外情況。
501 未實現 請求未完成。伺服器不支援所需的功能。
502 錯誤閘道器 請求未完成。伺服器從上游伺服器收到無效響應。
503 服務不可用 請求未完成。伺服器暫時過載或宕機。
504 閘道器超時 閘道器已超時。
505 不支援的 HTTP 版本 伺服器不支援“http 協議”版本。

設定 HTTP 狀態碼的方法

可以在 Servlet 程式中使用以下方法設定 HTTP 狀態碼。這些方法可與HttpServletResponse物件一起使用。

編號 方法和描述
1

public void setStatus ( int statusCode )

該方法設定一個任意的狀態碼。setStatus 方法採用一個整數(狀態碼)作為引數。如果您的響應包含特殊狀態碼和文件,請務必在使用PrintWriter返回任何內容之前呼叫 setStatus。

2

public void sendRedirect(String url)

該方法生成一個 302 響應以及指定新文件網址的位置標頭

3

public void sendError(int code, String message)

該方法傳送一個狀態碼(通常為 404)以及一個簡短訊息,該訊息會自動格式化在 HTML 文件中併發送到客戶端。

示例 HTTP 狀態碼

以下是傳送 407 錯誤碼到客戶端瀏覽器的示例,瀏覽器將顯示“需要身份驗證!!!”的訊息。

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

// Extend HttpServlet class
public class showError extends HttpServlet {
 
   // Method to handle GET method request.
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // Set error code and reason.
      response.sendError(407, "Need authentication!!!" );
   }
   
   // Method to handle POST method request.
   public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      doGet(request, response);
   }
}

現在呼叫上述 servlet 將顯示以下結果 −

HTTP Status 407 - Need authentication!!!

type Status report

messageNeed authentication!!!

descriptionThe client must first authenticate itself with the proxy (Need authentication!!!).

Apache Tomcat/5.5.29

Servlet - 編寫過濾器

Servlet 過濾器是可以用於 Servlet 程式設計的 Java 類,用於以下目的 −

  • 在客戶端訪問後端資源之前攔截來自客戶端的請求。

  • 在將伺服器上的響應傳送回客戶端之前對其進行處理。

規範中建議使用各種型別的過濾器 −

  • 身份驗證過濾器。
  • 資料壓縮過濾器。
  • 加密過濾器。
  • 觸發資源訪問事件的過濾器。
  • 影像轉換過濾器。
  • 日誌記錄和審計過濾器。
  • MIME 型別鏈過濾器。
  • 令牌化過濾器。
  • 轉換 XML 內容的 XSLT 過濾器。

過濾器部署在部署描述符檔案 web.xml 中,然後對映到應用程式的部署描述符中的 servlet 名稱或 URL 模式。

當 Web 容器啟動 Web 應用程式時,它會為在部署描述符中宣告的每個過濾器建立一個例項。過濾器按其在部署描述符中宣告的順序執行。

Servlet 過濾器方法

過濾器只是實現 javax.servlet.Filter 介面的 Java 類。javax.servlet.Filter 介面定義了三個方法 −

編號 方法和描述
1

public void doFilter (ServletRequest, ServletResponse, FilterChain)

當由於客戶端請求鏈末尾的資源而將請求/響應對透過該鏈傳遞時,容器每次都會呼叫此方法。

2

public void init(FilterConfig filterConfig)

Web 容器呼叫此方法向過濾器指示它正在投入服務。

3

public void destroy()

Web 容器呼叫此方法向過濾器指示它正在退出服務。

Servlet 過濾器 − 示例

以下是要列印客戶端 IP 地址和當前日期時間的 Servlet 過濾器示例。此示例將讓你對 Servlet 過濾器有一個基本的瞭解,但你可以使用相同的概念編寫更復雜的過濾器應用程式 −

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

// Implements Filter class
public class LogFilter implements Filter  {
   public void  init(FilterConfig config) throws ServletException {
      
      // Get init parameter 
      String testParam = config.getInitParameter("test-param"); 

      //Print the init parameter 
      System.out.println("Test Param: " + testParam); 
   }
   
   public void  doFilter(ServletRequest request, ServletResponse response,
      FilterChain chain) throws java.io.IOException, ServletException {

      // Get the IP address of client machine.
      String ipAddress = request.getRemoteAddr();

      // Log the IP address and current timestamp.
      System.out.println("IP "+ ipAddress + ", Time " + new Date().toString());

      // Pass request back down the filter chain
      chain.doFilter(request,response);
   }

   public void destroy( ) {
      /* Called before the Filter instance is removed from service by the web container*/
   }
}

以通常方式編譯 LogFilter.java,並將類檔案放在 <Tomcat 安裝目錄>/webapps/ROOT/WEB-INF/classes 中

Web.xml 中的 Servlet 過濾器對映

定義過濾器並將其對映到 URL 或 Servlet,方式與定義 Servlet 並將其對映到 URL 模式大致相同。在部署描述符檔案 web.xml 中為 filter 標記建立以下條目

<filter>
   <filter-name>LogFilter</filter-name>
   <filter-class>LogFilter</filter-class>
   <init-param>
      <param-name>test-param</param-name>
      <param-value>Initialization Paramter</param-value>
   </init-param>
</filter>

<filter-mapping>
   <filter-name>LogFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

上述過濾器將適用於所有 servlet,因為我們在配置中指定了 /*。只有在想要對幾個 servlet 應用過濾器時,才能指定特定的 servlet 路徑。

現在嘗試以通常的方式呼叫任何 servlet,你將在 Web 伺服器日誌中看到生成的日誌。你可以使用 Log4J 記錄器將上述日誌記錄到單獨的檔案中。

使用多個過濾器

Web 應用程式可以定義幾個具有特定目的的不同過濾器。假設你定義了兩個過濾器 AuthenFilterLogFilter。除你需建立不同的對映(如下所示)外,其餘過程仍保持與上面解釋的一樣 −

<filter>
   <filter-name>LogFilter</filter-name>
   <filter-class>LogFilter</filter-class>
   <init-param>
      <param-name>test-param</param-name>
      <param-value>Initialization Paramter</param-value>
   </init-param>
</filter>

<filter>
   <filter-name>AuthenFilter</filter-name>
   <filter-class>AuthenFilter</filter-class>
   <init-param>
      <param-name>test-param</param-name>
      <param-value>Initialization Paramter</param-value>
   </init-param>
</filter>

<filter-mapping>
   <filter-name>LogFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

<filter-mapping>
   <filter-name>AuthenFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

過濾器應用程式順序

web.xml 中 filter-mapping 元素的順序決定了 Web 容器將過濾器應用於 servlet 的順序。要反轉過濾器的順序,只需反轉 web.xml 檔案中的 filter-mapping 元素順序。

例如,上面的示例將首先應用 LogFilter,然後它將 AuthenFilter 應用於任何 servlet,但以下示例將反轉此順序 −

<filter-mapping>
   <filter-name>AuthenFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

<filter-mapping>
   <filter-name>LogFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

Servlet - 異常處理

當 Servlet 丟擲異常時,Web 容器會搜尋 web.xml 中與丟擲的異常型別匹配的配置中的 exception-type 元素。

你必須使用 web.xml 中的 error-page 元素來指定對某些 異常 或 HTTP 狀態程式碼 進行 Servlet 呼叫。

web.xml 配置

考慮一下,你有一個 ErrorHandler servlet,每當出現任何已定義的異常或錯誤時都會呼叫它。以下將在 web.xml 中建立的條目。

<!-- servlet definition -->
<servlet>
   <servlet-name>ErrorHandler</servlet-name>
   <servlet-class>ErrorHandler</servlet-class>
</servlet>

<!-- servlet mappings -->
<servlet-mapping>
   <servlet-name>ErrorHandler</servlet-name>
   <url-pattern>/ErrorHandler</url-pattern>
</servlet-mapping>

<!-- error-code related error pages -->
<error-page>
   <error-code>404</error-code>
   <location>/ErrorHandler</location>
</error-page>

<error-page>
   <error-code>403</error-code>
   <location>/ErrorHandler</location>
</error-page>

<!-- exception-type related error pages -->
<error-page>
   <exception-type>
      javax.servlet.ServletException
   </exception-type >
   <location>/ErrorHandler</location>
</error-page>

<error-page>
   <exception-type>java.io.IOException</exception-type >
   <location>/ErrorHandler</location>
</error-page>

如果你希望對所有異常有一個通用的 Error Handler,那麼你應該定義以下 error-page,而不是為每個異常定義單獨的 error-page 元素 −

<error-page>
   <exception-type>java.lang.Throwable</exception-type >
   <location>/ErrorHandler</location>
</error-page>

以下是關於用於異常處理的 web.xml 的注意事項 −

  • Servlet ErrorHandler 以與任何其他 servlet 相同的方式定義且在 web.xml 中配置。

  • 如果狀態程式碼出現任何錯誤,無論是 404(未找到)還是 403(禁止訪問),ErrorHandler servlet 都會被呼叫。

  • 如果 Web 應用程式丟擲 ServletException 或 IOException,那麼 Web 容器會呼叫 /ErrorHandler servlet。

  • 你可以定義不同的 Error Handler 來處理不同型別的錯誤或異常。上面的例子非常通用,希望它起作用,向你解釋了基本概念。

請求屬性 − 錯誤/異常

以下是一組錯誤處理 servlet 可訪問以分析錯誤/異常性質的請求屬性。

編號 屬性 & 說明
1

javax.servlet.error.status_code

此屬性給出狀態程式碼,可在儲存到 java.lang.Integer 資料型別之後進行儲存和分析。

2

javax.servlet.error.exception_type

此屬性提供有關異常型別的資訊,可在儲存到 java.lang.Class 資料型別之後進行儲存和分析。

3

javax.servlet.error.message

此屬性提供確切的錯誤訊息,可在儲存到 java.lang.String 資料型別之後進行儲存和分析。

4

javax.servlet.error.request_uri

此屬性提供有關呼叫 servlet 的 URL 的資訊,可在儲存到 java.lang.String 資料型別之後進行儲存和分析。

5

javax.servlet.error.exception

此屬性提供有關引發異常的資訊,其可進行儲存和分析。

6

javax.servlet.error.servlet_name

此屬性提供 servlet 名稱,可在儲存到 java.lang.String 資料型別之後進行儲存和分析。

錯誤處理程式 Servlet 示例

此示例將讓你基本瞭解 Servlet 中的異常處理,但你可以使用同一概念編寫更復雜的過濾器應用程式 −

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

// Extend HttpServlet class
public class ErrorHandler extends HttpServlet {
 
   // Method to handle GET method request.
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
         
      // Analyze the servlet exception       
      Throwable throwable = (Throwable)
      request.getAttribute("javax.servlet.error.exception");
      Integer statusCode = (Integer)
      request.getAttribute("javax.servlet.error.status_code");
      String servletName = (String)
      request.getAttribute("javax.servlet.error.servlet_name");
         
      if (servletName == null) {
         servletName = "Unknown";
      }
      String requestUri = (String)
      request.getAttribute("javax.servlet.error.request_uri");
      
      if (requestUri == null) {
         requestUri = "Unknown";
      }

      // Set response content type
      response.setContentType("text/html");

      PrintWriter out = response.getWriter();
      String title = "Error/Exception Information";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " +
         "transitional//en\">\n";
         
      out.println(docType +
         "<html>\n" +
         "<head><title>" + title + "</title></head>\n" +
         "<body bgcolor = \"#f0f0f0\">\n");

      if (throwable == null && statusCode == null) {
         out.println("<h2>Error information is missing</h2>");
         out.println("Please return to the <a href=\"" + 
            response.encodeURL("https://:8080/") + 
            "\">Home Page</a>.");
      } else if (statusCode != null) {
         out.println("The status code : " + statusCode);
      } else {
         out.println("<h2>Error information</h2>");
         out.println("Servlet Name : " + servletName + "</br></br>");
         out.println("Exception Type : " + throwable.getClass( ).getName( ) + "</br></br>");
         out.println("The request URI: " + requestUri + "<br><br>");
         out.println("The exception message: " + throwable.getMessage( ));
      }
      out.println("</body>");
      out.println("</html>");
   }
   
   // Method to handle POST method request.
   public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      doGet(request, response);
   }
}

以通常方式編譯 ErrorHandler.java 並將類檔案放在/webapps/ROOT/WEB-INF/classes 中。

讓我們在 web.xml 中新增以下配置來處理異常 −

<servlet>
   <servlet-name>ErrorHandler</servlet-name>
   <servlet-class>ErrorHandler</servlet-class>
</servlet>

<!-- servlet mappings -->
<servlet-mapping>
   <servlet-name>ErrorHandler</servlet-name>
   <url-pattern>/ErrorHandler</url-pattern>
</servlet-mapping>

<error-page>
   <error-code>404</error-code>
   <location>/ErrorHandler</location>
</error-page>

<error-page>
   <exception-type>java.lang.Throwable</exception-type >
   <location>/ErrorHandler</location>
</error-page>

現在,嘗試使用引發任何異常的 servlet 或鍵入錯誤 URL,這將觸發 Web 容器來呼叫 ErrorHandler servlet 並顯示程式設計後的適當訊息。例如,如果你鍵入錯誤的 URL,那麼它將顯示以下結果 −

The status code : 404

上述程式碼可能不適用於某些 Web 瀏覽器。因此請使用 Mozilla 和 Safari,它們應該起作用。

Servlet - Cookie 處理

Cookie 是儲存在客戶端電腦上的文字檔案,用於各種資訊跟蹤目的。Java Servlet 透明地支援 HTTP cookie。

識別返回使用者涉及三個步驟 −

  • 伺服器指令碼傳送一組 cookie 到瀏覽器。例如,姓名、年齡或身份證號等。

  • 瀏覽器為將來使用而在本地機器上儲存此資訊。

  • 當瀏覽器下次傳送任何請求到 Web 伺服器時,它會發送那些 cookie 資訊到伺服器,伺服器使用該資訊來識別使用者。

本節將指導你如何設定或重置 cookie,如何訪問 cookie 以及如何刪除 cookie。

Cookie 的解剖

Cookies通常設定在HTTP頭中(儘管JavaScript也可以直接在瀏覽器上設定Cookie)。設定Cookie的Servlet可能會發送類似如下內容的頭

HTTP/1.1 200 OK
Date: Fri, 04 Feb 2000 21:03:38 GMT
Server: Apache/1.3.9 (UNIX) PHP/4.0b3
Set-Cookie: name = xyz; expires = Friday, 04-Feb-07 22:03:38 GMT; 
   path = /; domain = tutorialspoint.com
Connection: close
Content-Type: text/html

您可以看到,Set-Cookie頭包含一個名稱-值對、一個GMT日期、一個路徑和一個域名。名稱和值將採用URL編碼。expires欄位是給瀏覽器的指令,在給定時間和日期後“忘記”Cookie。

如果瀏覽器被配置為儲存Cookie,那麼它會一直保留此資訊直到到期日期。如果使用者將瀏覽器指向與Cookie的路徑和域名匹配的任何頁面,它會向伺服器重新發送Cookie。瀏覽器的頭可能類似於此內容

GET / HTTP/1.0
Connection: Keep-Alive
User-Agent: Mozilla/4.6 (X11; I; Linux 2.2.6-15apmac ppc)
Host: zink.demon.co.uk:1126
Accept: image/gif, */*
Accept-Encoding: gzip
Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-8
Cookie: name = xyz

然後Servlet可以訪問Cookie,透過請求方法request.getCookies()返回一個Cookie物件陣列。

Servlet Cookie方法

以下是有用的方法列表,您可以在Servlet中使用它來操作Cookie。

編號 方法和描述
1

public void setDomain(String pattern)

此方法設定Cookie適用的域,例如tutorialspoint.com。

2

public String getDomain()

此方法獲取Cookie適用的域,例如tutorialspoint.com。

3

public void setMaxAge(int expiry)

此方法設定在Cookie過期之前經過的時間(以秒為單位)。如果您沒有設定此項,Cookie將僅在當前會話中持續。

4

public int getMaxAge()

此方法返回Cookie的最大生存時間,以秒為單位。預設情況下,-1表示Cookie將持續到瀏覽器關閉。

5

public String getName()

此方法返回Cookie的名稱。名稱在建立後不能更改。

6

public void setValue(String newValue)

此方法設定與Cookie關聯的值。

7

public String getValue()

此方法獲取與Cookie關聯的值。

8

public void setPath(String uri)

此方法設定此Cookie適用的路徑。如果您沒有指定路徑,Cookie將在當前頁面的相同目錄以及所有子目錄中的所有URL中返回。

9

public String getPath()

此方法獲取此Cookie適用的路徑。

10

public void setSecure(boolean flag)

此方法設定布林值,指示Cookie是否僅應透過加密(即SSL)連線傳送。

11

public void setComment(String purpose)

此方法指定一條描述Cookie目的的評論。如果瀏覽器向用戶顯示Cookie,此評論很有用。

12

public String getComment()

此方法返回描述此Cookie目的的評論,如果Cookie沒有評論,則返回null。

使用Servlet設定Cookie

使用Servlet設定Cookie涉及三個步驟−

(1) 建立一個Cookie物件 − 您對Cookie建構函式呼叫一個Cookie名稱和一個Cookie值,它們都是字串。

Cookie cookie = new Cookie("key","value");

請記住,名稱和值均不應包含空格或以下任何字元−

[ ] ( ) = , " / ? @ : ;

(2) 設定最大生存時間 − 您使用setMaxAge來指定Cookie應有效的時長(以秒為單位)。以下將設定一個24小時的Cookie。

cookie.setMaxAge(60 * 60 * 24); 

(3) 將Cookie傳送到HTTP響應頭中 − 您使用response.addCookie將Cookie新增到HTTP響應頭中,如下所示−

response.addCookie(cookie);

示例

讓我們修改我們的表單示例,以設定名字和姓氏的Cookie。

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
 
// Extend HttpServlet class
public class HelloForm extends HttpServlet {

   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // Create cookies for first and last names.      
      Cookie firstName = new Cookie("first_name", request.getParameter("first_name"));
      Cookie lastName = new Cookie("last_name", request.getParameter("last_name"));

      // Set expiry date after 24 Hrs for both the cookies.
      firstName.setMaxAge(60*60*24);
      lastName.setMaxAge(60*60*24);

      // Add both the cookies in the response header.
      response.addCookie( firstName );
      response.addCookie( lastName );

      // Set response content type
      response.setContentType("text/html");
 
      PrintWriter out = response.getWriter();
      String title = "Setting Cookies Example";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n";
      
      out.println(docType +
         "<html>\n" +
            "<head>
               <title>" + title + "</title>
            </head>\n" +
            
            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1 align = \"center\">" + title + "</h1>\n" +
               "<ul>\n" +
                  "  <li><b>First Name</b>: "
                  + request.getParameter("first_name") + "\n" +
                  "  <li><b>Last Name</b>: "
                  + request.getParameter("last_name") + "\n" +
               "</ul>\n" +
            "</body>
         </html>"
      );
   }
}

編譯上面的 Servlet HelloForm 並建立 web.xml 檔案中的適當條目,最後嘗試以下 HTML 頁面來呼叫 Servlet。

 
<html>
   <body>
      <form action = "HelloForm" method = "GET">
         First Name: <input type = "text" name = "first_name">
         <br />
         Last Name: <input type = "text" name = "last_name" />
         <input type = "submit" value = "Submit" />
      </form>
   </body>
</html>

將上面的 HTML 內容保留在 Hello.htm 檔案中,並將其放入 <Tomcat 安裝目錄>/webapps/ROOT 目錄下。當你訪問 https://:8080/Hello.htm 時,這裡有以上表單的實際輸出。


姓氏

嘗試輸入名字和姓氏,然後單擊提交按鈕。這將在你的螢幕上顯示名字和姓氏,同時還會設定兩個 Cookie firstName 和 lastName,下次單擊提交按鈕時它們將被傳回到伺服器。

下一部分將向你解釋如何在你的 Web 應用程式中訪問這些 Cookie。

使用 Servlet 讀取 Cookie

若要讀取 Cookie,你需要透過呼叫 HttpServletRequestgetCookies() 方法來建立 javax.servlet.http.Cookie 物件的陣列。然後迴圈遍歷該陣列,並使用 getName() 和 getValue() 方法來訪問每個 Cookie 和相關值。

示例

讓我們讀取在前面的示例中設定的 Cookie −

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
 
// Extend HttpServlet class
public class ReadCookies extends HttpServlet {
 
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      Cookie cookie = null;
      Cookie[] cookies = null;

      // Get an array of Cookies associated with this domain
      cookies = request.getCookies();

      // Set response content type
      response.setContentType("text/html");

      PrintWriter out = response.getWriter();
      String title = "Reading Cookies Example";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " +
         "transitional//en\">\n";
         
      out.println(docType +
         "<html>\n" +
         "<head><title>" + title + "</title></head>\n" +
         "<body bgcolor = \"#f0f0f0\">\n" );

      if( cookies != null ) {
         out.println("<h2> Found Cookies Name and Value</h2>");

         for (int i = 0; i < cookies.length; i++) {
            cookie = cookies[i];
            out.print("Name : " + cookie.getName( ) + ",  ");
            out.print("Value: " + cookie.getValue( ) + " <br/>");
         }
      } else {
         out.println("<h2>No cookies founds</h2>");
      }
      out.println("</body>");
      out.println("</html>");
   }
}

編譯上面的 Servlet ReadCookies 並建立 web.xml 檔案中的適當條目。如果你將 first_name Cookie 設定為“John”,將 last_name Cookie 設定為“Player”,那麼執行 https://:8080/ReadCookies 將顯示以下結果 −

Found Cookies Name and Value

Name : first_name, Value: John
Name : last_name, Value: Player

使用 Servlet 刪除 Cookie

刪除 Cookie 非常簡單。如果你想刪除一個 Cookie,那麼你只需要遵循以下三個步驟 −

  • 讀取一個已經存在的 Cookie 並將其儲存在 Cookie 物件中。

  • 使用 setMaxAge() 方法將 Cookie 年齡設定為零,以刪除現有 Cookie

  • 將此 Cookie 添加回響應標頭。

示例

以下示例將刪除一個名為“first_name”的現有 Cookie,當你下次執行 ReadCookies Servlet 時,它將為 first_name 返回 null 值。

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
 
// Extend HttpServlet class
public class DeleteCookies extends HttpServlet {
 
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      Cookie cookie = null;
      Cookie[] cookies = null;
         
      // Get an array of Cookies associated with this domain
      cookies = request.getCookies();

      // Set response content type
      response.setContentType("text/html");
 
      PrintWriter out = response.getWriter();
      String title = "Delete Cookies Example";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n";
         
      out.println(docType +
         "<html>\n" +
         "<head><title>" + title + "</title></head>\n" +
         "<body bgcolor = \"#f0f0f0\">\n" );
         
      if( cookies != null ) {
         out.println("<h2> Cookies Name and Value</h2>");

         for (int i = 0; i < cookies.length; i++) {
            cookie = cookies[i];

            if((cookie.getName( )).compareTo("first_name") == 0 ) {
               cookie.setMaxAge(0);
               response.addCookie(cookie);
               out.print("Deleted cookie : " + cookie.getName( ) + "<br/>");
            }
            out.print("Name : " + cookie.getName( ) + ",  ");
            out.print("Value: " + cookie.getValue( )+" <br/>");
         }
      } else {
         out.println("<h2>No cookies founds</h2>");
      }
      out.println("</body>");
      out.println("</html>");
   }
}

編譯上面的 Servlet DeleteCookies 並建立 web.xml 檔案中的適當條目。現在執行 https://:8080/DeleteCookies 將顯示以下結果 −

Cookies Name and Value

Deleted cookie : first_name

Name : first_name, Value: John

Name : last_name, Value: Player

現在嘗試執行 https://:8080/ReadCookies,它將只顯示一個 Cookie,如下所示 −

Found Cookies Name and Value

Name : last_name, Value: Player

你可以在 Internet Explorer 中手動刪除你的 Cookie。從工具選單開始,然後選擇 Internet 選項。若要刪除所有 Cookie,請按刪除 Cookie。

Servlet - 會話跟蹤

HTTP 是一種“無狀態”協議,這意味著每次客戶端檢索一個網頁時,客戶端都會向該 Web 伺服器開啟一個單獨的連線,並且該伺服器自動不會保留以前客戶端請求的任何記錄。

不過,以下三種方法仍可維護 Web 客戶端和 Web 伺服器之間的會話 −

Cookie

Web 伺服器可以為每個 Web 客戶端分配一個唯一的會話 ID 作為 Cookie,並且在來自該客戶端的後續請求中,可以使用收到的 Cookie 來識別它們。

這可能不是一種有效的方法,因為很多時候瀏覽器不支援 Cookie,所以我不建議使用此過程來維護會話。

隱藏的表單欄位

Web 伺服器可以傳送一個隱藏的 HTML 表單欄位,其中包含一個唯一的會話 ID,如下所示 −

<input type = "hidden" name = "sessionid" value = "12345">

此條目表示,當表單提交時,指定的名稱和值會自動包含在 GET 或 POST 資料中。每次 Web 瀏覽器傳送回請求時,session_id 值都可用於跟蹤不同的 Web 瀏覽器。

這可能是一種跟蹤會話的有效方法,但單擊常規 (<A HREF...>) 超文字連結不會導致表單提交,所以隱藏表單欄位也不能支援常規會話跟蹤。

URL 重寫

你可以在每個 URL 的末尾附加一些標識會話的額外資料,並且伺服器可以將該會話識別符號與它已儲存的對該會話的資料關聯起來。

例如,對於 https://tutorialspoint.tw/file.htm;sessionid = 12345,會話識別符號附加為 sessionid = 12345,可以在 Web 伺服器上訪問它以識別客戶端。

URL 重寫是維持會話的一種更好的方式,它甚至可以在瀏覽器不支援 Cookie 時生效。URL 重寫的缺點是,您必須動態生成每個 URL 才能分配會話 ID,即使是一個簡單的靜態 HTML 頁面也一樣。

HttpSession 物件

除了上述三種方式之外,Servlet 還提供 HttpSession 介面,它提供了一種跨越某個 Web 站點的多於一個頁面請求或訪問來識別使用者,並存儲該使用者相關資訊的方法。

Servlet 容器使用此介面在 HTTP 客戶端和 HTTP 伺服器之間建立一個會話。會話會持續一定時間,跨越使用者的多個連線或頁面請求。

您可以透過呼叫 HttpServletRequest 的公有方法 getSession() 獲取 HttpSession 物件,如下所示 −

HttpSession session = request.getSession();

在向客戶端傳送任何文件內容之前,您需要呼叫 request.getSession()。以下是透過 HttpSession 物件可用的重要方法摘要 −

編號 方法和描述
1

public Object getAttribute(String name)

此方法返回在此會話中與指定名稱繫結的物件,或者如果未繫結任何物件,則返回 null。

2

public Enumeration getAttributeNames()

此方法返回包含繫結到此會話的所有物件的名稱的字串物件 Enumeration。

3

public long getCreationTime()

此方法返回此會話的建立時間,以自 1970 年 1 月 1 日格林威治時間午夜以來經過的毫秒為單位。

4

public String getId()

此方法返回包含分配給此會話的唯一識別符號的字串。

5

public long getLastAccessedTime()

此方法返回會話的最後訪問時間,格式為自 1970 年 1 月 1 日格林威治時間午夜以來經過的毫秒。

6

public int getMaxInactiveInterval()

此方法返回會話容器在客戶端訪問之間保持會話開啟的最大時間間隔(秒)。

7

public void invalidate()

此方法使此會話無效,並解綁繫結到它的任何物件。

8

public boolean isNew(

如果客戶端尚未了解此會話,或如果客戶端選擇不加入此會話,此方法將返回 true。

9

public void removeAttribute(String name)

此方法從此會話中移除與指定名稱繫結的物件。

10

public void setAttribute(String name, Object value)

此方法使用指定名稱將物件繫結到此會話。

11

public void setMaxInactiveInterval(int interval)

此方法指定客戶端請求之間的時間(秒),在此時間之後,Servlet 容器將使此會話失效。

會話跟蹤示例

此示例介紹如何使用 HttpSession 物件查詢會話的建立時間和上次訪問時間。如果尚未存在,我們將與該請求關聯一個新的會話。

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
 
// Extend HttpServlet class
public class SessionTrack extends HttpServlet {
 
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
         
      // Create a session object if it is already not  created.
      HttpSession session = request.getSession(true);
         
      // Get session creation time.
      Date createTime = new Date(session.getCreationTime());
         
      // Get last access time of this web page.
      Date lastAccessTime = new Date(session.getLastAccessedTime());

      String title = "Welcome Back to my website";
      Integer visitCount = new Integer(0);
      String visitCountKey = new String("visitCount");
      String userIDKey = new String("userID");
      String userID = new String("ABCD");

      // Check if this is new comer on your web page.
      if (session.isNew()) {
         title = "Welcome to my website";
         session.setAttribute(userIDKey, userID);
      } else {
         visitCount = (Integer)session.getAttribute(visitCountKey);
         visitCount = visitCount + 1;
         userID = (String)session.getAttribute(userIDKey);
      }
      session.setAttribute(visitCountKey,  visitCount);

      // Set response content type
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();

      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " +
         "transitional//en\">\n";

      out.println(docType +
         "<html>\n" +
            "<head><title>" + title + "</title></head>\n" +
            
            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1 align = \"center\">" + title + "</h1>\n" +
               "<h2 align = \"center\">Session Infomation</h2>\n" +
               "<table border = \"1\" align = \"center\">\n" +
                  
                  "<tr bgcolor = \"#949494\">\n" +
                     "  <th>Session info</th><th>value</th>
                  </tr>\n" +
                     
                  "<tr>\n" +
                     "  <td>id</td>\n" +
                     "  <td>" + session.getId() + "</td>
                  </tr>\n" +
                  
                  "<tr>\n" +
                     "  <td>Creation Time</td>\n" +
                     "  <td>" + createTime + "  </td>
                  </tr>\n" +
                  
                  "<tr>\n" +
                     "  <td>Time of Last Access</td>\n" +
                     "  <td>" + lastAccessTime + "  </td>
                  </tr>\n" +
                  
                  "<tr>\n" +
                     "  <td>User ID</td>\n" +
                     "  <td>" + userID + "  </td>
                  </tr>\n" +
                  
                  "<tr>\n" +
                     "  <td>Number of visits</td>\n" +
                     "  <td>" + visitCount + "</td>
                  </tr>\n" +
               "</table>\n" +
            "</body>
         </html>"
      );
   }
}

編譯上述 servlet SessionTrack,並在 web.xml 檔案中建立相應的條目。現在,在第一次執行時,執行 https://:8080/SessionTrack 將顯示以下結果 −

Welcome to my website

Session Infomation

Session info value
id 0AE3EC93FF44E3C525B4351B77ABB2D5
Creation Time Tue Jun 08 17:26:40 GMT+04:00 2010
Time of Last Access Tue Jun 08 17:26:40 GMT+04:00 2010
User ID ABCD
Number of visits 0

現在嘗試第二次運行同一 servlet,它將顯示以下結果。

Welcome Back to my website

Session Infomation

info type value
id 0AE3EC93FF44E3C525B4351B77ABB2D5
Creation Time Tue Jun 08 17:26:40 GMT+04:00 2010
Time of Last Access Tue Jun 08 17:26:40 GMT+04:00 2010
User ID ABCD
Number of visits 1

刪除會話資料

處理完使用者的會話資料後,您有幾個選擇 −

  • 刪除特定屬性 − 您可以呼叫 public void removeAttribute(String name) 方法來刪除與特定鍵關聯的值。

  • 刪除整個會話 − 您可以呼叫 public void invalidate() 方法來丟棄整個會話。

  • 設定會話超時 − 您可以呼叫public void setMaxInactiveInterval(int interval) 方法來分別設定會話的超時時間。

  • 讓使用者退出 − 支援 servlets 2.4 的伺服器,您可以呼叫 logout 讓客戶端退出 Web 伺服器,並使所有使用者的所有會話失效。

  • web.xml 配置 − 如果您正在使用 Tomcat,除了上面提到的方法之外,您還可以按照如下方式在 web.xml 檔案中配置會話超時。

<session-config>
   <session-timeout>15</session-timeout>
</session-config>

超時以分鐘表示,並覆蓋 Tomcat 中的預設超時 30 分鐘。

Servlet 中的 getMaxInactiveInterval( ) 方法以秒為單位返回該會話的超時期限。因此,如果您的會話在 web.xml 中配置為 15 分鐘,getMaxInactiveInterval( ) 會返回 900。

Servlet - 資料庫訪問

本教程假定您瞭解 JDBC 應用程式的工作方式。在透過 servlet 訪問資料庫之前,請確保您設定了適當的 JDBC 環境以及資料庫。

有關如何使用 JDBC 訪問資料庫及其環境設定的更多詳細資訊,您可以透過我們的JDBC 教程瞭解更多資訊。

首先了解基本概念,讓我們建立一個簡單表格,並在該表格中建立幾條記錄,如下所示 −

建立表格

要建立 TEST 資料庫中的Employees 表格,請使用以下步驟 −

步驟 1

開啟一個命令提示符 並更改為安裝目錄,如下所示 −

C:\>
C:\>cd Program Files\MySQL\bin
C:\Program Files\MySQL\bin>

步驟 2

登入到資料庫,如下所示

C:\Program Files\MySQL\bin>mysql -u root -p
Enter password: ********
mysql>

步驟 3

TEST 資料庫中建立表格Employee,如下所示 −

mysql> use TEST;
mysql> create table Employees (
   id int not null,
   age int not null,
   first varchar (255),
   last varchar (255)
);
Query OK, 0 rows affected (0.08 sec)
mysql>

建立資料記錄

最後,您在 Employee 表格中建立了幾條記錄,如下所示 −

mysql> INSERT INTO Employees VALUES (100, 18, 'Zara', 'Ali');
Query OK, 1 row affected (0.05 sec)
 
mysql> INSERT INTO Employees VALUES (101, 25, 'Mahnaz', 'Fatma');
Query OK, 1 row affected (0.00 sec)
 
mysql> INSERT INTO Employees VALUES (102, 30, 'Zaid', 'Khan');
Query OK, 1 row affected (0.00 sec)
 
mysql> INSERT INTO Employees VALUES (103, 28, 'Sumit', 'Mittal');
Query OK, 1 row affected (0.00 sec)
 
mysql>

訪問資料庫

這裡有一個示例,展示瞭如何使用 Servlet 訪問 TEST 資料庫。

// Loading required libraries
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.sql.*;
 
public class DatabaseAccess extends HttpServlet{

   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
   
      // JDBC driver name and database URL
      static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";  
      static final String DB_URL="jdbc:mysql:///TEST";

      //  Database credentials
      static final String USER = "root";
      static final String PASS = "password";

      // Set response content type
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();
      String title = "Database Result";
      
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n";
      
      out.println(docType +
         "<html>\n" +
         "<head><title>" + title + "</title></head>\n" +
         "<body bgcolor = \"#f0f0f0\">\n" +
         "<h1 align = \"center\">" + title + "</h1>\n");
      try {
         // Register JDBC driver
         Class.forName("com.mysql.jdbc.Driver");

         // Open a connection
         Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);

         // Execute SQL query
         Statement stmt = conn.createStatement();
         String sql;
         sql = "SELECT id, first, last, age FROM Employees";
         ResultSet rs = stmt.executeQuery(sql);

         // Extract data from result set
         while(rs.next()){
            //Retrieve by column name
            int id  = rs.getInt("id");
            int age = rs.getInt("age");
            String first = rs.getString("first");
            String last = rs.getString("last");

            //Display values
            out.println("ID: " + id + "<br>");
            out.println(", Age: " + age + "<br>");
            out.println(", First: " + first + "<br>");
            out.println(", Last: " + last + "<br>");
         }
         out.println("</body></html>");

         // Clean-up environment
         rs.close();
         stmt.close();
         conn.close();
      } catch(SQLException se) {
         //Handle errors for JDBC
         se.printStackTrace();
      } catch(Exception e) {
         //Handle errors for Class.forName
         e.printStackTrace();
      } finally {
         //finally block used to close resources
         try {
            if(stmt!=null)
               stmt.close();
         } catch(SQLException se2) {
         } // nothing we can do
         try {
            if(conn!=null)
            conn.close();
         } catch(SQLException se) {
            se.printStackTrace();
         } //end finally try
      } //end try
   }
} 

現在讓我們編譯上述 servlet 並建立 web.xml 中的以下條目

....
<servlet>
   <servlet-name>DatabaseAccess</servlet-name>
   <servlet-class>DatabaseAccess</servlet-class>
</servlet>
 
<servlet-mapping>
   <servlet-name>DatabaseAccess</servlet-name>
   <url-pattern>/DatabaseAccess</url-pattern>
</servlet-mapping>
....

現在使用 URL https://:8080/DatabaseAccess 呼叫此 servlet,該 URL 將顯示以下響應 −

Database Result

ID: 100, Age: 18, First: Zara, Last: Ali ID: 101, Age: 25, First: Mahnaz, Last: Fatma ID: 102, Age: 30, First: Zaid, Last: Khan ID: 103, Age: 28, First: Sumit, Last: Mittal

Servlet - 檔案上載

Servlet 可以與 HTML 表單標記一起使用,以允許使用者將檔案上傳到伺服器。上傳的檔案可能是文字檔案、影像檔案或任何文件。

建立檔案上傳表單

以下 HTM 程式碼建立了一個上傳程式表單。以下是需要注意的重要事項 −

  • 表單method 屬性應設定為POST 方法,不能使用 GET 方法

  • 表單enctype 屬性應設定為multipart/form-data

  • 表單action 屬性應設定為一個 servlet 檔案,該檔案將在後端伺服器處理檔案上傳。以下示例正在使用 UploadServlet servlet 上傳檔案。

  • 要上傳單個檔案,您應該使用單個 <input .../> 標記,其屬性 type="file"。要允許上傳多個檔案,請包含具有 name 屬性不同值的多於一個的 input 標記。瀏覽器將會與每個標記關聯一個瀏覽按鈕。

 
<html>
   <head>
      <title>File Uploading Form</title>
   </head>
   
   <body>
      <h3>File Upload:</h3>
      Select a file to upload: <br />
      <form action = "UploadServlet" method = "post" enctype = "multipart/form-data">
         <input type = "file" name = "file" size = "50" />
         <br />
         <input type = "submit" value = "Upload File" />
      </form>
   </body>
</html>

這將顯示以下結果,該結果允許從本地 PC 中選擇一個檔案,當用戶點選“上傳檔案”時,將提交表單以及選定的檔案 −

 
File Upload: 
Select a file to upload: 


NOTE: This is just dummy form and would not work.

編寫後端 servlet

以下 servlet 為 UploadServlet,它將負責接受上傳的檔案並將其儲存在 <Tomcat-installation-directory>/webapps/data。該目錄名稱也可以使用外部配置新增,例如 web.xml 中的 context-param 元素,如下所示 −

 
<web-app>
   ....
   <context-param> 
      <description>Location to store uploaded file</description> 
      <param-name>file-upload</param-name> 
      <param-value>
         c:\apache-tomcat-5.5.29\webapps\data\
     </param-value> 
   </context-param>
   ....
</web-app>

以下是 UploadServlet 的原始碼,它一次可以處理多個檔案上傳。在繼續之前,您需要確保以下事項 −

  • 以下示例取決於 FileUpload,因此請確保您的類路徑中具有 commons-fileupload.x.x.jar 檔案的最新版本。您可以從 https://commons.apache.org/fileupload/ 下載它。

  • FileUpload 依賴 Commons IO,因此請確保類路徑中包含最新版本的 commons-io-x.x.jar 檔案。可以從 https://commons.apache.org/io/ 下載該檔案。

  • 在測試以下示例時,應當上傳一個大小小於 maxFileSize 的檔案,否則檔案將無法上傳。

  • 請確保提前建立目錄 c:\temp 和 c:\apache-tomcat8.0.28\webapps\data。

// Import required java libraries
import java.io.*;
import java.util.*;
 
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.output.*;

public class UploadServlet extends HttpServlet {
   
   private boolean isMultipart;
   private String filePath;
   private int maxFileSize = 50 * 1024;
   private int maxMemSize = 4 * 1024;
   private File file ;

   public void init( ){
      // Get the file location where it would be stored.
      filePath = getServletContext().getInitParameter("file-upload"); 
   }
   
   public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, java.io.IOException {
   
      // Check that we have a file upload request
      isMultipart = ServletFileUpload.isMultipartContent(request);
      response.setContentType("text/html");
      java.io.PrintWriter out = response.getWriter( );
   
      if( !isMultipart ) {
         out.println("<html>");
         out.println("<head>");
         out.println("<title>Servlet upload</title>");  
         out.println("</head>");
         out.println("<body>");
         out.println("<p>No file uploaded</p>"); 
         out.println("</body>");
         out.println("</html>");
         return;
      }
  
      DiskFileItemFactory factory = new DiskFileItemFactory();
   
      // maximum size that will be stored in memory
      factory.setSizeThreshold(maxMemSize);
   
      // Location to save data that is larger than maxMemSize.
      factory.setRepository(new File("c:\\temp"));

      // Create a new file upload handler
      ServletFileUpload upload = new ServletFileUpload(factory);
   
      // maximum file size to be uploaded.
      upload.setSizeMax( maxFileSize );

      try { 
         // Parse the request to get file items.
         List fileItems = upload.parseRequest(request);
	
         // Process the uploaded file items
         Iterator i = fileItems.iterator();

         out.println("<html>");
         out.println("<head>");
         out.println("<title>Servlet upload</title>");  
         out.println("</head>");
         out.println("<body>");
   
         while ( i.hasNext () ) {
            FileItem fi = (FileItem)i.next();
            if ( !fi.isFormField () ) {
               // Get the uploaded file parameters
               String fieldName = fi.getFieldName();
               String fileName = fi.getName();
               String contentType = fi.getContentType();
               boolean isInMemory = fi.isInMemory();
               long sizeInBytes = fi.getSize();
            
               // Write the file
               if( fileName.lastIndexOf("\\") >= 0 ) {
                  file = new File( filePath + fileName.substring( fileName.lastIndexOf("\\"))) ;
               } else {
                  file = new File( filePath + fileName.substring(fileName.lastIndexOf("\\")+1)) ;
               }
               fi.write( file ) ;
               out.println("Uploaded Filename: " + fileName + "<br>");
            }
         }
         out.println("</body>");
         out.println("</html>");
         } catch(Exception ex) {
            System.out.println(ex);
         }
      }
      
      public void doGet(HttpServletRequest request, HttpServletResponse response)
         throws ServletException, java.io.IOException {

         throw new ServletException("GET method used with " +
            getClass( ).getName( )+": POST method required.");
      }
   }
}

編譯和執行 Servlet

編譯以上 servlet UploadServlet,並按照以下方式在 web.xml 檔案中建立必需的條目。

 
<servlet>
   <servlet-name>UploadServlet</servlet-name>
   <servlet-class>UploadServlet</servlet-class>
</servlet>

<servlet-mapping>
   <servlet-name>UploadServlet</servlet-name>
   <url-pattern>/UploadServlet</url-pattern>
</servlet-mapping>

現在嘗試使用上面建立的 HTML 表單上傳檔案。當你嘗試訪問 https://:8080/UploadFile.htm 時,將顯示以下結果,它將幫助你從本地計算機上傳任何檔案。

 
File Upload: 

Select a file to upload:


如果 servlet 指令碼正常執行,則檔案應上傳到 c:\apache-tomcat8.0.28\webapps\data\ 目錄中。

Servlet - 處理日期

使用 Servlet 最重要的優勢之一在於,你可以使用核心 Java 中提供的大多數方法。本教程將帶你瞭解 Java 中提供的 Date 類,該類位於 java.util 包中,此類封裝了當前日期和時間。

Date 類支援兩個建構函式。第一個建構函式用當前日期和時間初始化該物件。

Date( )

以下構造器接受一個自 1970 年 1 月 1 日午夜以來經過的毫秒數作為引數

Date(long millisec)

獲得 Date 物件後,你可以呼叫以下任何支援方法來處理日期 -

編號 方法及說明
1

boolean after(Date date)

如果呼叫 Date 物件包含的日期早於指定日期,則返回 true,否則返回 false。

2

boolean before(Date date)

如果呼叫 Date 物件包含的日期晚於指定日期,則返回 true,否則返回 false。

3

Object clone( )

複製呼叫 Date 物件。

4

int compareTo(Date date)

將呼叫物件的日期值與 date 的值進行比較。如果值相等,則返回 0。如果呼叫物件早於 date,則返回一個負值。如果呼叫物件晚於 date,則返回一個正值。

5

int compareTo(Object obj)

如果 obj 是 Date 類的例項,則與 compareTo(Date) 運作方式相同。否則,將引發 ClassCastException。

6

boolean equals(Object date)

如果呼叫 Date 物件包含的時間和日期與指定日期相同,則返回 true,否則返回 false。

7

long getTime( )

返回自 1970 年 1 月 1 日以來經過的毫秒數。

8

int hashCode( )

返回呼叫物件的雜湊碼。

9

void setTime(long time)

將時間與日期設定為指定的時間,它表示自 1970 年 1 月 1 日午夜以來經過的毫秒數。

10

String toString( )

將呼叫 Date 物件轉換為字串並返回結果。

獲取當前日期和時間

在 Java Servlet 中獲取當前日期和時間非常容易。可以使用一個簡單的 Date 物件與 toString() 方法列印當前日期和時間,如下所示 -

// Import required java libraries
import java.io.*;
import java.util.Date;
import javax.servlet.*;
import javax.servlet.http.*;
 
// Extend HttpServlet class
public class CurrentDate extends HttpServlet {
 
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // Set response content type
      response.setContentType("text/html");
 
      PrintWriter out = response.getWriter();
      String title = "Display Current Date & Time";
      Date date = new Date();
      String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n";
      
      out.println(docType +
         "<html>\n" +
            "<head><title>" + title + "</title></head>\n" +
            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1 align = \"center\">" + title + "</h1>\n" +
               "<h2 align = \"center\">" + date.toString() + "</h2>\n" +
            "</body>
         </html>"
      );
   }
}

現在,讓我們編譯上面的 Servlet 並建立 web.xml 中的相應項,然後使用 URL https://:8080/CurrentDate 來呼叫該 Servlet。這將產生以下結果:

Display Current Date & Time

Mon Jun 21 21:46:49 GMT+04:00 2010

嘗試重新整理 URL https://:8080/CurrentDate,每次重新整理時你都會發現秒數有所不同。

日期比較

正如我上面提到的,你可以在 Servlet 中使用所有可用的 Java 方法。如果你需要比較兩個日期,以下是這些方法:

  • 你可以使用 getTime( ) 來獲取自 1970 年 1 月 1 日午夜以來所經過的毫秒數,併為這兩個物件獲取這些值,然後比較這兩個值。

  • 你可以使用 before( )、after( ) 和 equals( ) 方法。因為該月的第 12 天在第 18 天之前,例如,new Date(99, 2, 12).before(new Date (99, 2, 18)) 返回 true。

  • 你可以使用 compareTo( ) 方法,它由 Comparable 介面定義,Date 實現了該介面。

使用 SimpleDateFormat 設定日期格式

SimpleDateFormat 是一個具體的類,用於以區域敏感的方式設定和解析日期。SimpleDateFormat 允許你選擇任何使用者自定義的日期時間格式模式作為起始模式。

讓我們修改上面的示例,如下所示:

// Import required java libraries
import java.io.*;
import java.text.*;
import java.util.Date;
import javax.servlet.*;
import javax.servlet.http.*;
 
// Extend HttpServlet class
public class CurrentDate extends HttpServlet {
 
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // Set response content type
      response.setContentType("text/html");
 
      PrintWriter out = response.getWriter();
      String title = "Display Current Date & Time";
      Date dNow = new Date( );
      SimpleDateFormat ft = new SimpleDateFormat ("E yyyy.MM.dd 'at' hh:mm:ss a zzz");
      String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n";
      
      out.println(docType +
         "<html>\n" +
            "<head><title>" + title + "</title></head>\n" +
            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1 align = \"center\">" + title + "</h1>\n" +
               "<h2 align = \"center\">" + ft.format(dNow) + "</h2>\n" +
            "</body>
         </html>"
      );
   }
}

再次編譯上面的 Servlet,然後使用 URL https://:8080/CurrentDate 來呼叫該 Servlet。這將產生以下結果:

Display Current Date & Time

Mon 2010.06.21 at 10:06:44 PM GMT+04:00

簡單日期格式化程式碼

要指定時間格式,請使用時間模式字串。在此模式中,所有 ASCII 字母均保留為模式字母,定義如下:

字元 描述 示例
G 時代標誌 AD
y 四位數年份 2001
M 年份中的月份 七月或 07
d 月份中的日期 10
h 上午/下午時刻 (1~12) 12
H 一整天中的時刻 (0~23) 22
m 小時中的分鐘 30
s 分鐘中的秒 55
S 毫秒 234
E 週中的日期 星期二
D 年份中的日期 360
F 月份中的一週中的日期 2(7 月中的第二個星期三)
w 年份中的星期 40
W 月份中的星期 1
a 上午/下午標記 下午
k 一整天中的時刻 (1~24) 24
K 上午/下午時刻 (0~11) 10
z 時區 美國東部標準時間
' 換行符文字 分隔符
" 單引號 `

有關用於操作日期的常量可用方法的完整列表,你可以參考標準 Java 文件。

Servlet - 頁面重定向

頁面重定向是一種將客戶端傳送到實際請求以外的新位置的技術。文件移至新位置時,或可能是由於負載平衡而導致頁面重定向通常被使用。

將請求重定向到另一頁的最簡單的方法是使用響應物件的 sendRedirect() 方法。以下是此方法的簽名:

public void HttpServletResponse.sendRedirect(String location) 
throws IOException 

此方法會將響應連同狀態程式碼和新頁面位置一起傳送回瀏覽器。你還可以將 setStatus() 和 setHeader() 方法一起使用,以實現相同的效果:

.... 
String site = "http://www.newpage.com" ; 
response.setStatus(response.SC_MOVED_TEMPORARILY); 
response.setHeader("Location", site);  
.... 

示例

此示例展示了 Servlet 如何對另一個位置執行頁面重定向:

import java.io.*;
import java.sql.Date;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class PageRedirect extends HttpServlet {
    
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

      // Set response content type
      response.setContentType("text/html");

      // New location to be redirected
      String site = new String("http://www.photofuntoos.com");

      response.setStatus(response.SC_MOVED_TEMPORARILY);
      response.setHeader("Location", site);    
   }
} 

現在讓我們編譯上述 servlet 並建立 web.xml 中的以下條目

....
<servlet>
   <servlet-name>PageRedirect</servlet-name>
   <servlet-class>PageRedirect</servlet-class>
</servlet>

<servlet-mapping>
   <servlet-name>PageRedirect</servlet-name>
   <url-pattern>/PageRedirect</url-pattern>
</servlet-mapping>
....

現在,使用 URL https://:8080/PageRedirect 呼叫此 Servlet。這會將你重定向到 URL http://www.photofuntoos.com。

Servlet - 訪問計數器

網頁的點選計數器

很多時候,你可能希望知道網站上某個特定頁面上的總點選次數。使用 Servlet 來計算這些點選次數非常簡單,因為 Servlet 的生命週期由其執行其中的容器控制。

以下是實現基於 Servlet 生命週期的一個簡單頁面點選計數器的步驟:

  • 在 init() 方法中初始化一個全域性變數。

  • 每次呼叫 doGet() 或 doPost() 方法時,增加全域性變數。

  • 需要時,你可在 destroy() 方法中使用資料庫表儲存全域性變數的值。此值可在 servlet 下次初始化時在 init() 方法中讀取。此步驟是可選的。

  • 如果你只想統計會話中唯一的頁面點選量,則可以使用 isNew() 方法檢查同一頁面是否已在此會話中點選過。此步驟是可選的。

  • 你可以顯示全域性計數器的值以顯示網站上的總點選量。此步驟也是可選的。

此處我假設 Web 容器不會重新啟動。如果重新啟動或 servlet 已銷燬,點選計數器將重置。

示例

此示例展示如何實現一個簡單的頁面點選計數器 −

import java.io.*;
import java.sql.Date;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class PageHitCounter extends HttpServlet {

   private int hitCount; 

   public void init() { 
      // Reset hit counter.
      hitCount = 0;
   } 

   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

      // Set response content type
      response.setContentType("text/html");

      // This method executes whenever the servlet is hit 
      // increment hitCount 
      hitCount++; 
      PrintWriter out = response.getWriter();
      String title = "Total Number of Hits";
      String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n";
      
      out.println(docType +
         "<html>\n" +
            "<head><title>" + title + "</title></head>\n" +
            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1 align = \"center\">" + title + "</h1>\n" +
               "<h2 align = \"center\">" + hitCount + "</h2>\n" +
            "</body>
         </html>"
      );
   }
   
   public void destroy() { 
      // This is optional step but if you like you
      // can write hitCount value in your database.
   } 
} 

現在讓我們編譯上述 servlet 並建立 web.xml 中的以下條目

<servlet>
   <servlet-name>PageHitCounter</servlet-name>
   <servlet-class>PageHitCounter</servlet-class>
</servlet>

<servlet-mapping>
   <servlet-name>PageHitCounter</servlet-name>
   <url-pattern>/PageHitCounter</url-pattern>
</servlet-mapping>
....

現在使用該 URL 呼叫此 servlet https://:8080/PageHitCounter。每次重新整理此頁面,計數器都會加 1,並將顯示以下結果 −

Total Number of Hits

6

Hit Counter for a Website:

很多時候,你可能想知道整個網站的總點選量。這在 Servlet 中也是非常簡單的,我們可以使用過濾器來實現。

以下是基於過濾器生命週期實現一個簡單的網站點選計數器的步驟 −

  • 在過濾器的 init() 方法中初始化一個全域性變數。

  • 每次呼叫 doFilter 方法時將全域性變數加 1。

  • 需要時,你可在過濾器的 destroy() 方法中使用資料庫表儲存全域性變數的值。此值可在過濾器下次初始化時在 init() 方法中讀取。此步驟是可選的。

此處我假設 Web 容器不會重新啟動。如果重新啟動或 servlet 已銷燬,點選計數器將重置。

示例

此示例展示如何實現一個簡單的網站點選計數器 −

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

public class SiteHitCounter implements Filter {

   private int hitCount; 

   public void  init(FilterConfig config) throws ServletException {
      // Reset hit counter.
      hitCount = 0;
   }

   public void  doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
      throws java.io.IOException, ServletException {

      // increase counter by one
      hitCount++;

      // Print the counter.
      System.out.println("Site visits count :"+ hitCount );

      // Pass request back down the filter chain
      chain.doFilter(request,response);
   }
   
   public void destroy() { 
      // This is optional step but if you like you
      // can write hitCount value in your database.
   } 
} 

現在讓我們編譯以上 servlet,並在 web.xml 中建立以下條目

....
<filter>
   <filter-name>SiteHitCounter</filter-name>
   <filter-class>SiteHitCounter</filter-class>
</filter>

<filter-mapping>
   <filter-name>SiteHitCounter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>
....

現在呼叫任意 URL,如 URL https://:8080/。每次任何頁面獲得點選,計數器都會加 1,並且會在日誌中顯示以下訊息 −

Site visits count : 1
Site visits count : 2
Site visits count : 3
Site visits count : 4
Site visits count : 5
..................

Servlets - 自動頁面重新整理

考慮一個顯示即時遊戲比分、股票市場狀態或貨幣兌換比率的網頁。對於所有此類型別的頁面,你需要使用瀏覽器的重新整理或重新載入按鈕定期重新整理網頁。

Java Servlet 透過提供一種機制讓工作變得容易,你可以透過這種機制以這樣的方式製作網頁,即它可在給定的時間間隔後自動重新整理。

重新整理網頁最簡單的方法是使用響應物件的 setIntHeader() 方法。以下是此方法的簽名 −

public void setIntHeader(String header, int headerValue)

此方法向瀏覽器傳送標頭“重新整理”以及一個整數,該整數表示以秒為單位的時間間隔。

自動頁面重新整理示例

此示例展示了 servlet 如何使用 setIntHeader() 方法設定 重新整理 標頭執行自動頁面重新整理。

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
 
// Extend HttpServlet class
public class Refresh extends HttpServlet {
 
   // Method to handle GET method request.
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // Set refresh, autoload time as 5 seconds
      response.setIntHeader("Refresh", 5);
 
      // Set response content type
      response.setContentType("text/html");
 
      // Get current time
      Calendar calendar = new GregorianCalendar();
      String am_pm;
      int hour = calendar.get(Calendar.HOUR);
      int minute = calendar.get(Calendar.MINUTE);
      int second = calendar.get(Calendar.SECOND);
      
      if(calendar.get(Calendar.AM_PM) == 0)
        am_pm = "AM";
      else
        am_pm = "PM";
 
      String CT = hour+":"+ minute +":"+ second +" "+ am_pm;
    
      PrintWriter out = response.getWriter();
      String title = "Auto Page Refresh using Servlet";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n";
      
      out.println(docType +
         "<html>\n" +
         "<head><title>" + title + "</title></head>\n"+
         "<body bgcolor = \"#f0f0f0\">\n" +
         "<h1 align = \"center\">" + title + "</h1>\n" +
         "<p>Current Time is: " + CT + "</p>\n"
      );
   }
   
   // Method to handle POST method request.
   public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      doGet(request, response);
   }
}

現在讓我們編譯以上 servlet,並在 web.xml 中建立以下條目

....
 <servlet>
     <servlet-name>Refresh</servlet-name>
     <servlet-class>Refresh</servlet-class>
 </servlet>
 
 <servlet-mapping>
     <servlet-name>Refresh</servlet-name>
     <url-pattern>/Refresh</url-pattern>
 </servlet-mapping>
....

現在使用 URL https://:8080/Refresh 呼叫此 servlet,它會每 5 秒顯示一次當前系統時間,如下所示。只需執行 servlet 並等待檢視結果 −

Auto Page Refresh using Servlet

Current Time is: 9:44:50 PM

Servlet - 傳送電子郵件

使用 Servlet 傳送電子郵件很容易,但首先你應在你的機器上安裝 JavaMail APIJava Activation Framework (JAF)

下載並解壓這些檔案,在新建的頂級目錄中,您會找到適用於這兩個應用程式的 jar 檔案。您需要在 CLASSPATH 中新增 mail.jaractivation.jar 檔案。

傳送一個簡單的電子郵件

以下是從您的機器傳送簡單電子郵件的示例。這裡假定您的 本地主機已連線到網際網路,有足夠的能力傳送電子郵件。同時確保 Java 電子郵件 API 包和 JAF 包的所有 jar 檔案都可用於 CLASSPATH。

// File Name SendEmail.java
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;
 
public class SendEmail extends HttpServlet {

   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // Recipient's email ID needs to be mentioned.
      String to = "abcd@gmail.com";
 
      // Sender's email ID needs to be mentioned
      String from = "web@gmail.com";
 
      // Assuming you are sending email from localhost
      String host = "localhost";
 
      // Get system properties
      Properties properties = System.getProperties();
 
      // Setup mail server
      properties.setProperty("mail.smtp.host", host);
 
      // Get the default Session object.
      Session session = Session.getDefaultInstance(properties);
      
      // Set response content type
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();

      try {
         // Create a default MimeMessage object.
         MimeMessage message = new MimeMessage(session);
         
         // Set From: header field of the header.
         message.setFrom(new InternetAddress(from));
         
         // Set To: header field of the header.
         message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
         
         // Set Subject: header field
         message.setSubject("This is the Subject Line!");
         
         // Now set the actual message
         message.setText("This is actual message");
         
         // Send message
         Transport.send(message);
         String title = "Send Email";
         String res = "Sent message successfully....";
         String docType =
            "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n";
         
         out.println(docType +
            "<html>\n" +
               "<head><title>" + title + "</title></head>\n" +
               "<body bgcolor = \"#f0f0f0\">\n" +
                  "<h1 align = \"center\">" + title + "</h1>\n" +
                  "<p align = \"center\">" + res + "</p>\n" +
               "</body>
            </html>"
         );
      } catch (MessagingException mex) {
         mex.printStackTrace();
      }
   }
} 

現在讓我們編譯以上 servlet,並在 web.xml 中建立以下條目

....
 <servlet>
   <servlet-name>SendEmail</servlet-name>
   <servlet-class>SendEmail</servlet-class>
</servlet>
 
<servlet-mapping>
   <servlet-name>SendEmail</servlet-name>
   <url-pattern>/SendEmail</url-pattern>
</servlet-mapping>
....

現在使用 URL https://:8080/SendEmail 呼叫此 Servlet,它會向給定的電子郵件 ID abcd@gmail.com 傳送電子郵件並顯示以下響應−

Send Email

Sent message successfully....

如果您想向多個收件人傳送電子郵件,那麼將使用以下方法來指定多個電子郵件 ID −

void addRecipients(Message.RecipientType type, Address[] addresses)
throws MessagingException

以下是對引數的描述 −

  • 型別 − 這將設定為 TO、CC 或 BCC。這裡的 CC 表示抄送,BCC 表示密送。示例:Message.RecipientType.TO

  • 地址 − 這是電子郵件 ID 陣列。您需要在指定電子郵件 ID 時使用 InternetAddress() 方法。

傳送一封 HTML 電子郵件

以下是從您的機器傳送 HTML 電子郵件的示例。這裡假定您的 本地主機已連線到網際網路,有足夠的能力傳送電子郵件。同時確保 Java 電子郵件 API 包和 JAF 包的所有 jar 檔案都可用於 CLASSPATH。

除了這裡我們使用 setContent() 方法來設定內容(其第二個引數是“text/html”)以指定訊息中包含 HTML 內容外,此示例與上一個示例非常相似。

您可以使用此示例傳送任意大的 HTML 內容。

// File Name SendEmail.java
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;
 
public class SendEmail extends HttpServlet {
    
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // Recipient's email ID needs to be mentioned.
      String to = "abcd@gmail.com";
 
      // Sender's email ID needs to be mentioned
      String from = "web@gmail.com";
 
      // Assuming you are sending email from localhost
      String host = "localhost";
 
      // Get system properties
      Properties properties = System.getProperties();
 
      // Setup mail server
      properties.setProperty("mail.smtp.host", host);
 
      // Get the default Session object.
      Session session = Session.getDefaultInstance(properties);
      
      // Set response content type
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();

      try {
         
         // Create a default MimeMessage object.
         MimeMessage message = new MimeMessage(session);
         
         // Set From: header field of the header.
         message.setFrom(new InternetAddress(from));
         
         // Set To: header field of the header.
         message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
         // Set Subject: header field
         message.setSubject("This is the Subject Line!");

         // Send the actual HTML message, as big as you like
         message.setContent("<h1>This is actual message</h1>", "text/html" );
         
         // Send message
         Transport.send(message);
         String title = "Send Email";
         String res = "Sent message successfully....";
         String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n";
         
         out.println(docType +
            "<html>\n" +
               "<head><title>" + title + "</title></head>\n" +
               "<body bgcolor = \"#f0f0f0\">\n" +
                  "<h1 align = \"center\">" + title + "</h1>\n" +
                  "<p align = \"center\">" + res + "</p>\n" +
               "</body>
            </html>"
         );
      } catch (MessagingException mex) {
         mex.printStackTrace();
      }
   }
} 

編譯並執行上述 Servlet 以在給定的電子郵件 ID 上傳送 HTML 訊息。

在電子郵件中傳送附件

以下是使用您的機器傳送帶附件的電子郵件的示例。這裡假定您的 本地主機已連線到網際網路,有足夠的能力傳送電子郵件。

// File Name SendEmail.java
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;
 
public class SendEmail extends HttpServlet {

   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // Recipient's email ID needs to be mentioned.
      String to = "abcd@gmail.com";
 
      // Sender's email ID needs to be mentioned
      String from = "web@gmail.com";
 
      // Assuming you are sending email from localhost
      String host = "localhost";

      // Get system properties
      Properties properties = System.getProperties();
 
      // Setup mail server
      properties.setProperty("mail.smtp.host", host);
 
      // Get the default Session object.
      Session session = Session.getDefaultInstance(properties);
      
	  // Set response content type
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();

      try {
         // Create a default MimeMessage object.
         MimeMessage message = new MimeMessage(session);
 
         // Set From: header field of the header.
         message.setFrom(new InternetAddress(from));
 
         // Set To: header field of the header.
         message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
 
         // Set Subject: header field
         message.setSubject("This is the Subject Line!");
 
         // Create the message part 
         BodyPart messageBodyPart = new MimeBodyPart();
 
         // Fill the message
         messageBodyPart.setText("This is message body");
         
         // Create a multipar message
         Multipart multipart = new MimeMultipart();
 
         // Set text message part
         multipart.addBodyPart(messageBodyPart);
 
         // Part two is attachment
         messageBodyPart = new MimeBodyPart();
         String filename = "file.txt";
         DataSource source = new FileDataSource(filename);
         messageBodyPart.setDataHandler(new DataHandler(source));
         messageBodyPart.setFileName(filename);
         multipart.addBodyPart(messageBodyPart);
 
         // Send the complete message parts
         message.setContent(multipart );
 
         // Send message
         Transport.send(message);
         String title = "Send Email";
         String res = "Sent message successfully....";
         String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n";
         
         out.println(docType +
            "<html>\n" +
               "<head><title>" + title + "</title></head>\n" +
               "<body bgcolor = \"#f0f0f0\">\n" +
                  "<h1 align = \"center\">" + title + "</h1>\n" +
                  "<p align = \"center\">" + res + "</p>\n" +
               "</body>
            </html>"
         );
      } catch (MessagingException mex) {
         mex.printStackTrace();
      }
   }
} 

編譯並執行上面的 servlet 以傳送一個檔案作為附件以及在給定的電子郵件 ID 上傳送的訊息。

使用者身份驗證部分

如果需要向電子郵件伺服器提供使用者 ID 和密碼進行身份驗證,則可以按如下方式設定這些屬性 −

 props.setProperty("mail.user", "myuser");
 props.setProperty("mail.password", "mypwd");

電子郵件傳送機制的其餘部分將保持如上所述內容。

Servlet - 打包

包含 WEB-INF 子目錄的 Web 應用程式結構對所有 Java Web 應用程式來說都是標準的,並由 servlet API 規範指定。假設頂級目錄名為 myapp。此目錄結構如下所示 −

/myapp
   /images
   /WEB-INF
      /classes
      /lib

WEB-INF 子目錄包含應用程式的部署描述符,名為 web.xml。所有 HTML 檔案都應儲存在頂級目錄中,即 myapp。對於管理員使用者,您會發現 ROOT 目錄作為父目錄。

用包建立 Servlet

WEB-INF/classes 目錄包含所有 servlet 類和其他類檔案,其結構與包名稱相匹配。例如,如果您具有com.myorg.MyServlet 的完全限定類名,則此 servlet 類必須位於如下目錄中 −

/myapp/WEB-INF/classes/com/myorg/MyServlet.class 

以下是使用包名com.myorg 建立 MyServlet 類的方法

// Name your package
package com.myorg;  

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
 
public class MyServlet extends HttpServlet {
 
   private String message;
 
   public void init() throws ServletException {
      // Do required initialization
      message = "Hello World";
   }
 
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // Set response content type
      response.setContentType("text/html");
 
      // Actual logic goes here.
      PrintWriter out = response.getWriter();
      out.println("<h1>" + message + "</h1>");
   }

   public void destroy() {
      // do nothing.
   }
}

編譯有包的 Servlet

用包中的類編譯不會有什麼不同。最簡單的方法是將 Java 檔案儲存在完全限定路徑中,如上面提到的類將儲存在 com.myorg 中。您還需要在 CLASSPATH 中新增此目錄。

假定您的環境已正確設定,進入<Tomcat-installationdirectory>/webapps/ROOT/WEB-INF/classes 目錄並如下編譯 MyServlet.java

$ javac MyServlet.java

如果 Servlet 依賴於任何其他庫,您還必須將這些 JAR 檔案包含在您的 CLASSPATH 中。我僅包含了 servlet-api.jar JAR 檔案,因為我在 Hello World 程式中不使用任何其他庫。

此命令列使用 Sun Microsystems Java 軟體開發包 (JDK) 附帶的內建 javac 編譯器。為了使此命令正常工作,您必須在 PATH 環境變數中包含您正在使用的 Java SDK 的位置。

如果一切順利,上面的編譯會在同一目錄中生成MyServlet.class檔案。下一部分將說明如何將編譯後的servlet部署到生產環境中。

已打包的Servlet部署

預設情況下,Servlet 應用程式位於路徑 <Tomcat-installationdirectory>/webapps/ROOT,類檔案將駐留在 <Tomcat-installationdirectory>/webapps/ROOT/WEB-INF/classes 中。

如果你有一個com.myorg.MyServlet的全限定類名,那麼這個Servlet類必須位於WEB-INF/classes/com/myorg/MyServlet.class中,並且你需要在位於<Tomcat-installationdirectory>/webapps/ROOT/WEB-INF/中的web.xml檔案新增以下條目:

<servlet>
   <servlet-name>MyServlet</servlet-name>
   <servlet-class>com.myorg.MyServlet</servlet-class>
</servlet>
 
<servlet-mapping>
   <servlet-name>MyServlet</servlet-name>
   <url-pattern>/MyServlet</url-pattern>
</servlet-mapping>

要建立在 web.xml 檔案中可用的 <web-app>...</web-app> 標記內部的上述條目。此表中可能已提供各種條目,但不必理會。

你幾乎大功告成了,現在讓我們使用<Tomcat-installationdirectory>\bin\startup.bat(在Windows上)或<Tomcat-installationdirectory>/bin/startup.sh(在Linux/Solaris等上)啟動tomcat伺服器,最後在瀏覽器的位址列中鍵入https://:8080/MyServlet。如果一切順利,你將得到以下結果−

Hello World

Servlet - 除錯

測試/除錯Servlet總是很困難。Servlet傾向於涉及大量的客戶端/伺服器互動,從而使得錯誤可能會出現,但難以重現。

這裡有一些提示和建議,可以在除錯中幫助你。

System.out.println()

System.out.println()很容易用作標記,以測試某個程式碼段是否正在執行。我們也可以打印出變數值。另外−

  • 由於System物件是核心Java物件的一部分,因此可以在任何地方使用它,而無需安裝任何額外的類。這包括Servlet、JSP、RMI、EJB、普通Bean和類以及獨立應用程式。

  • 在斷點處停止技術停止了正常執行,因此需要更多時間。而將內容寫入System.out不會過多幹擾應用程式的正常執行流,這使得它在時序至關重要的情況下非常有價值。

以下是使用System.out.println()的語法−

System.out.println("Debugging message");

由以上語法生成的所有訊息都將記錄在Web伺服器日誌檔案中。

訊息記錄

務必使用適當的記錄方法使用標準記錄方法記錄所有除錯、警告和錯誤訊息。我用log4J記錄了所有訊息。

Servlet API還提供了一種簡單的輸出資訊的方法,使用log()方法如下−

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class ContextLog extends HttpServlet {
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, java.io.IOException {
    
      String par = request.getParameter("par1");
      
      //Call the two ServletContext.log methods
      ServletContext context = getServletContext( );

      if (par == null || par.equals(""))
         //log version with Throwable parameter
         context.log("No message received:", new IllegalStateException("Missing parameter"));
      else
         context.log("Here is the visitor's message: " + par);
      
      response.setContentType("text/html");
      java.io.PrintWriter out = response.getWriter( );
      String title = "Context Log";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n";
      
      out.println(docType +
         "<html>\n" +
            "<head><title>" + title + "</title></head>\n" +
            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1 align = \"center\">" + title + "</h1>\n" +
               "<h2 align = \"center\">Messages sent</h2>\n" +
            "</body>
         </html>"
      );
   } //doGet
}

ServletContext將它的文字訊息記錄到servlet容器的日誌檔案中。對於Tomcat,這些日誌位於<Tomcat-installation-directory>/logs中。

日誌檔案確實給出了新出現的bug或問題的頻率的指示。因此,最好在異常的catch子句中使用log()函式,這些異常通常不應發生。

使用JDB偵錯程式

你可以用與除錯applet或應用程式相同的jdb命令來除錯servlet。

要除錯servlet,我們除錯sun.servlet.http.HttpServer,並在HttpServer響應瀏覽器發出的HTTP請求時執行servlet時仔細觀察。這與除錯applet非常相似。不同之處在於,對於applet,實際除錯程式是sun.applet.AppletViewer。

大多數偵錯程式會自動知道如何除錯applet,從而隱藏該詳細資訊。在它們對servlet執行相同操作之前,你必須透過執行以下操作來幫助你的偵錯程式−

  • 設定偵錯程式的類路徑,以便它可以找到sun.servlet.http.Http-Server和關聯類。

  • 設定偵錯程式的類路徑,以便它還可以找到你的servlets和支援類,通常是server_root/servlets和server_root/classes。

通常你不希望類路徑中包含server_root/servlets,因為它會停用servlet重新載入。但是,此包含對於除錯很有用。它允許你的偵錯程式在HttpServer中的自定義servlet載入器載入servlet之前在servlet中設定斷點。

一旦設定好正確的類路徑,就開始除錯 sun.servlet.http.HttpServer。你可以在你要除錯的任何 servlet 中設定斷點,然後使用 Web 瀏覽器向 HttpServer 發出給定 servlet 的請求(https://:8080/servlet/ServletToDebug)。你應該看到執行在你的斷點處停止。

使用註釋

程式碼中的註釋可以用多種方式幫助除錯過程。註釋在除錯過程中還有很多其他用法。

Servlet 使用 Java 註釋和單行(// ...)和多行(/* ... */)註釋可以暫時刪除你的 Java 程式碼的部分。如果漏洞消失了,仔細檢視你剛剛註釋的程式碼並找出問題。

客戶端和伺服器頭

有時當一個 servlet 沒有按預期工作時,檢視原始 HTTP 請求和響應會很有用。如果你熟悉 HTTP 的結構,你可以閱讀請求和響應並確切地知道這些標頭的情況。

重要的除錯技巧

以下是有關 servlet 除錯的一些其他除錯技巧列表 -

  • 記住,server_root/classes 不會重新載入,而 server_root/servlets 可能重新載入。

  • 要求瀏覽器顯示正在顯示的頁面的原始內容。這有助於識別格式問題。這通常是“檢視”選單下的一個選項。

  • 透過強制完全重新載入頁面,確保瀏覽器沒有快取前一個請求的輸出。在 Netscape Navigator 中,使用 Shift-Reload;在 Internet Explorer 中,使用 Shift-Refresh。

  • 驗證 servlet 的 init() 方法是否採用 ServletConfig 引數並立即呼叫 super.init(config)。

Servlet - 國際化

在我們繼續之前,讓我解釋三個重要術語 -

  • 國際化 (i18n) - 這意味著讓網站能夠提供翻譯成訪問者的語言或國籍的不同內容版本

  • 本地化 (l10n) - 這意味著將資源新增到網站以適應特定的地理或文化區域。

  • 區域設定 - 這是一個特定的文化或地理區域。它通常被稱為語言符號,後跟國家符號,它們用下劃線分隔。例如,“en_US”表示美國英語區域設定。

在搭建全球網站時,需要注意很多事項。本教程不會對此提供完整的詳細資訊,但它會為你提供一個很好的示例,說明你如何透過區分其位置(即區域設定)向網際網路社群以不同語言提供你的網頁。

servlet 可以根據請求者的區域設定選擇合適的網站版本,並根據當地語言、文化和要求提供合適的網站版本。以下是返回 Locale 物件的請求物件的方法。

java.util.Locale request.getLocale() 

檢測區域設定

以下是重要的區域設定方法,你可以使用這些方法來檢測請求者的位置、語言和區域設定。下面所有方法都顯示在請求者的瀏覽器中設定的國家名稱和語言名稱。

編號 方法和描述
1

String getCountry()

此方法以 ISO 3166 2 字母格式返回此區域設定的國家/地區程式碼(大寫)。

2

String getDisplayCountry()

此方法返回適合向用戶顯示的區域設定國家/地區名稱。

3

String getLanguage()

此方法以 ISO 639 格式返回此語言環境的語言程式碼(小寫)。

4

String getDisplayLanguage()

此方法返回適合使用者顯示的語言環境語言名稱。

5

String getISO3Country()

此方法返回此語言環境的國家/地區的三個字母縮寫。

6

String getISO3Language()

此方法返回此語言環境的語言的三個字母縮寫。

示例

此示例展示如何為某個請求顯示語言和關聯的國家/地區 −

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.Locale;

public class GetLocale extends HttpServlet {

   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
   
      //Get the client's Locale
      Locale locale = request.getLocale();
      String language = locale.getLanguage();
      String country = locale.getCountry();

      // Set response content type
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();

      String title = "Detecting Locale";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n";
      
      out.println(docType +
         "<html>\n" +
            "<head><title>" + title + "</title></head>\n" +
            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1 align = \"center\">" + language + "</h1>\n" +
               "<h2 align = \"center\">" + country + "</h2>\n" +
         "</body>
         </html>"
      );
   }
} 

語言設定

servlet 可輸出用西歐語言(如英語、西班牙語、德語、法語、義大利語、荷蘭語等)編寫的頁面。在此,設定 ContentLanguage 標頭以正確顯示所有字元非常重要。

第二點是使用 HTML 實體顯示所有特殊字元,例如“&#241;”表示“ñ”,而“&#161;”表示“¡”,如下所示

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.Locale;

public class DisplaySpanish extends HttpServlet {

   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

      // Set response content type
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();

      // Set spanish language code.
      response.setHeader("Content-Language", "es");

      String title = "En Espa&ntilde;ol";
      String docType =
      "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n";
      
      out.println(docType +
         "<html>\n" +
            "<head><title>" + title + "</title></head>\n" +
            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1>" + "En Espa&ntilde;ol:" + "</h1>\n" +
               "<h1>" + "&iexcl;Hola Mundo!" + "</h1>\n" +
            "</body>
         </html>"
      );
   }
} 

特定語言環境的日期

可以使用 java.text.DateFormat 類及其靜態方法 getDateTimeInstance() 格式化特定於語言環境的日期和時間。以下是展示如何特定於給定的語言環境格式化日期的示例 −

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.Locale;
import java.text.DateFormat;
import java.util.Date;

public class DateLocale extends HttpServlet {

   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // Set response content type
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();
      
      //Get the client's Locale
      Locale locale = request.getLocale( );
      String date = DateFormat.getDateTimeInstance(DateFormat.FULL, 
         DateFormat.SHORT, locale).format(new Date( ));

      String title = "Locale Specific Dates";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n";
     
      out.println(docType +
         "<html>\n" +
            "<head><title>" + title + "</title></head>\n" +
            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1 align = \"center\">" + date + "</h1>\n" +
            "</body>
         </html>"
      );
   }
} 

特定語言環境的貨幣

可以使用 java.txt.NumberFormat 類及其靜態方法 getCurrencyInstance() 格式化數字(例如 long 或 double 型別)為特定語言環境的貨幣。以下是展示如何特定於給定的語言環境格式化貨幣的示例 −

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.Locale;
import java.text.NumberFormat;
import java.util.Date;

public class CurrencyLocale extends HttpServlet {
    
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

      // Set response content type
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();

      //Get the client's Locale
      Locale locale = request.getLocale( );
      NumberFormat nft = NumberFormat.getCurrencyInstance(locale);
      String formattedCurr = nft.format(1000000);

      String title = "Locale Specific Currency";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n";
      
      out.println(docType +
         "<html>\n" +
            "<head><title>" + title + "</title></head>\n" +
            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1 align = \"center\">" + formattedCurr + "</h1>\n" +
            "</body>
         </html>"
      );
   }
} 

特定語言環境的百分比

可以使用 java.txt.NumberFormat 類及其靜態方法 getPercentInstance() 獲取特定語言環境的百分比。以下是展示如何特定於給定的語言環境格式化百分比的示例 −

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.Locale;
import java.text.NumberFormat;
import java.util.Date;

public class PercentageLocale extends HttpServlet {

   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // Set response content type
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();
      
      //Get the client's Locale
      Locale locale = request.getLocale( );
      NumberFormat nft = NumberFormat.getPercentInstance(locale);
      String formattedPerc = nft.format(0.51);

      String title = "Locale Specific Percentage";
      String docType =
      "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n";
      
      out.println(docType +
         "<html>\n" +
            "<head><title>" + title + "</title></head>\n" +
            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1 align = \"center\">" + formattedPerc + "</h1>\n" +
            "</body>
         </html>"
      );
   }
} 

Servlet - 標註

到目前為止,你已經瞭解了 Servlet 如何使用部署描述符 (web.xml 檔案) 將你的應用程式部署到 Web 伺服器中。Servlet API 3.0 引入了名為 javax.servlet.annotation 的新軟體包。它提供了一些註釋型別,可用於註釋 servlet 類。如果使用註釋,則不需要部署描述符 (web.xml)。但是你應該使用 tomcat7 或任何更高版本的 tomcat。

註釋可以替代 Web 部署描述符檔案 (web.xml) 中等效的 XML 配置,如 servlet 宣告和 servlet 對映。Servlet 容器將在部署時處理帶註釋的類。

Servlet 3.0 中引入的註釋型別有 −

編號 註釋和描述
1

@WebServlet

宣告 servlet。

2

@WebInitParam

指定初始化引數。

3

@WebFilter

宣告 servlet 過濾器。

4

@WebListener

宣告 WebListener

5

@HandlesTypes

宣告 ServletContainerInitializer 可以處理的類型別。

6

@HttpConstraint

此註釋在 ServletSecurity 註釋內使用,以表示要應用到所有 HTTP 協議方法的安全約束,ServletSecurity 註釋內沒有相應的 HttpMethodConstraint 元素。

7

@HttpMethodConstraint

此註釋在 ServletSecurity 註釋內使用,以表示對特定 HTTP 協議訊息的安全約束。

8

@MultipartConfig

可以在 Servlet 類中指定的註釋,表示 Servlet 的例項期望符合 multipart/form-data MIME 型別的請求。

9

@ServletSecurity

在 Servlet 實現類中使用此註釋指定 Servlet 容器對 HTTP 協議訊息強制執行的安全約束。

在此,我們詳細討論了一些註釋。

@WebServlet

@WebServlet 用於使用容器宣告 Servlet 的配置。下表包含了 WebServlet 註解中使用的屬性列表。

編號 屬性 & 說明
1

String name

Servlet 的名稱

2

String[] value

URL 模式陣列

3

String[] urlPatterns

此 Filter 應用到的 URL 模式陣列

4

Int loadOnStartup

整數值提供啟動順序提示

5

WebInitParam[] initParams

此 Servlet 的初始化引數陣列

6

Boolean asyncSupported

此 Servlet 支援的非同步操作

7

String smallIcon

此 Servlet 的小圖示,如果存在的話

8

String largeIcon

此 Servlet 的大圖示,如果存在的話

9

String description

此 Servlet 的描述,如果存在的話

10

String displayName

此 Servlet 的顯示名稱,如果存在的話

至少一個 URL 模式必須在註解的 valueurlPattern 屬性中宣告,但不能同時存在。

當 URL 模式是唯一要設定的屬性時,建議使用 value 屬性,否則應使用 urlPattern 屬性。

示例

以下示例介紹如何使用 @WebServlet 註解。它是一個簡單的 servlet,顯示文字 Hello Servlet

import java.io.IOException; 
import java.io.PrintWriter; 
import javax.servlet.ServletException; 
import javax.servlet.annotation.WebInitParam; 
import javax.servlet.annotation.WebServlet; 
import javax.servlet.http.HttpServlet; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
@WebServlet(value = "/Simple") 
public class Simple extends HttpServlet {

   private static final long serialVersionUID = 1L; 

   protected void doGet(HttpServletRequest request, HttpServletResponse response)  
      throws ServletException, IOException { 
   
      response.setContentType("text/html");   
      PrintWriter out = response.getWriter();   
      out.print("<html><body>");   
      out.print("<h3>Hello Servlet</h3>");   
      out.print("</body></html>");         
   }   
}

按照通常的方式編譯 Simple.java,並將你的類檔案放在 <Tomcat-installationdirectory>/webapps/ROOT/WEB-INF/classes 中。

現在嘗試透過僅僅執行 https://:8080/Simple 來呼叫任何 servlet。你將在網頁上看到以下輸出。

Hello servlet

@WebInitParam

@WebInitParam 註解用於為 Servlet 或 Filter 指定一個初始化引數。它在 WebFilter 或 WebSevlet 註解中使用。下表包含了 WebInitParam 註解中使用的屬性列表。

編號 屬性 & 說明
1

String name

初始化引數的名稱

2

String value

初始化引數的值

3

String description

初始化引數的描述

示例

以下示例介紹如何將 @WebInitParam 註解與 @WebServlet 註解一起使用。它是一個簡單的servlet,顯示文字 Hello Servlet 和從 init 引數中獲取的字串值 Hello World!

import java.io.IOException; 
import java.io.PrintWriter; 
import javax.servlet.ServletException; 
import javax.servlet.annotation.WebInitParam; 
import javax.servlet.annotation.WebServlet; 
import javax.servlet.http.HttpServlet; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse;

@WebServlet(value = "/Simple", initParams = { 
   @WebInitParam(name = "foo", value = "Hello "), 
   @WebInitParam(name = "bar", value = " World!") 
}) 
public class Simple extends HttpServlet {

   private static final long serialVersionUID = 1L; 

   protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {   
      
      response.setContentType("text/html");   
      PrintWriter out = response.getWriter();   
      out.print("<html><body>");   
      out.print("<h3>Hello Servlet</h3>");   
      out.println(getInitParameter("foo")); 
      out.println(getInitParameter("bar")); 
      out.print("</body></html>");         
   }   
}

按照通常的方式編譯 Simple.java,並將你的類檔案放在 <Tomcatinstallationdirectory>;/webapps/ROOT/WEB-INF/classes 中。

現在嘗試透過僅僅執行 https://:8080/Simple 來呼叫任何 servlet。你將在網頁上看到以下輸出。

Hello Servlet

Hello World! 

@Webfilter

這是用於宣告 servlet 過濾器的註解。它在部署時由容器處理,並將相應的過濾器應用到指定的 URL 模式、servlet 和排程程式型別中。

@WebFilter 註解在 Web 應用程式中定義過濾器。此註解指定在類中,包含有關正在宣告的過濾器的元資料。帶註解的過濾器必須指定至少一個 URL 模式。下表列出了 WebFilter 註解中使用的屬性。

編號 屬性 & 說明
1

String filterName

過濾器的名稱

2

String[] urlPatterns

提供應用過濾器的一系列值或 urlPatterns

3

DispatcherType[] dispatcherTypes

指定應用過濾器的排程程式(請求/響應)型別

4

String[] servletNames

提供一系列 servlet 名稱

5

String displayName

過濾器的名稱

6

String description

過濾器的描述

7

WebInitParam[] initParams

此過濾器的初始化引數陣列

8

Boolean asyncSupported

此過濾器支援的非同步操作

9

String smallIcon

此過濾器的圖示,如果存在的話

10

String largeIcon

此過濾器的圖示,如果存在的話

示例

以下示例介紹瞭如何使用 @WebFilter 註釋。這是一個簡單的 LogFilter,它會在控制檯顯示 Init-param test-param 的值以及當前時間戳。這意味著該過濾器在請求和響應之間充當介面層。這裡我們對 urlPattern 使用 “/*”。這意味著該過濾器適用於所有 servlet。

import java.io.IOException; 
import javax.servlet.annotation.WebFilter; 
import javax.servlet.annotation.WebInitParam; 
import javax.servlet.*; 
import java.util.*;  

// Implements Filter class

@WebFilter(urlPatterns = {"/*"}, initParams = { 
   @WebInitParam(name = "test-param", value = "Initialization Paramter")}) 
public class LogFilter implements Filter {
   
   public void init(FilterConfig config) throws ServletException { 
      // Get init parameter  
      String testParam = config.getInitParameter("test-param");
            
      //Print the init parameter  
      System.out.println("Test Param: " + testParam);  
   } 

   public void doFilter(ServletRequest request, ServletResponse response,
      FilterChain chain) throws IOException, ServletException { 
	  
      // Log the current timestamp. 
      System.out.println("Time " + new Date().toString());  
         
      // Pass request back down the filter chain 
      chain.doFilter(request,response); 
   }

   public void destroy( ) {
      /* Called before the Filter instance is removed  
      from service by the web container*/ 
   } 
}

按照通常的方式編譯 Simple.java,並將你的類檔案放在 <Tomcat-installationdirectory>/webapps/ROOT/WEB-INF/classes 中。

現在嘗試透過僅僅執行 https://:8080/Simple 來呼叫任何 servlet。你將在網頁上看到以下輸出。

Hello Servlet
  
Hello World!

現在,開啟 servlet 控制檯。在那裡,您將找到 init 引數 testparam當前時間戳 的值以及 servlet 通知訊息。

廣告