HttpServletRequest和HttpServletResponse物件詳解

2020-07-16 10:05:32
《Servlet生命週期》一文提到,在 Servlet 生命週期的執行階段,Servlet 容器會為當前的用戶端請求建立一個 HttpServletRequest 物件和一個 HttpServletResponse 物件(這兩個物件分別繼承自 ServletRequest 和 ServletResponse),其中:
  • HttpServletRequest 物件封裝了用戶端的請求資訊,比如 IP 地址、域名、埠號等,該訊息會附帶在 HTTP 請求中一起傳送給伺服器;
  • HttpServletResponse 物件用來封裝 HTTP 響應訊息,比如狀態碼、編碼格式、內容型別等,該訊息會跟隨 HTTP 請求一起傳送給用戶端。

HttpServletRequest 物件

HttpServletRequest 物件代表用戶端(瀏覽器)的請求,當用戶端通過 HTTP 協定存取伺服器時,HTTP 請求中的所有資訊都封裝在這個物件中,通過這個物件提供的方法,可以獲得用戶端請求的所有資訊。

以下是 HttpServletRequest 物件獲取請求資訊的一些方法以及程式碼範例:
獲取用戶端資訊
getRequestURL() 返回用戶端發出請求時的完整 URL。
getRequestURI() 返回請求行中的資源名部分。
getQueryString() 返回請求中的引數部分。
getRemoteAddr() 返回發出請求的客戶機的 IP 地址 。
getRemoteHost() 返回發出請求的客戶機的完整主機名。
getRemotePort() 返回客戶機所使用的網路埠號。
getLocalAddr() 返回 WEB 伺服器的 IP 地址。
getLocalName() 返回 WEB 伺服器的主機名 。
getMethod() 根據客戶機的請求方式,返回生成這個請求的HTTP方法名稱(GET、    POST等)。
獲得用戶端請求頭
getHeader(String name) 以 String 的形式返回指定請求頭的值。
getHeaders(String name) 以 String 物件的 Enumeration 形式返回指定請求頭的所有值。
getHeaderNames() 返回此請求包含的所有頭名稱的列舉。
獲得客戶機請求引數(用戶端提交的資料)
getParameter(String name) 根據 name 獲取請求引數的值。
getParameterMap() 返回的是一個 Map 型別的值,該返回值記錄著用戶端所提交的請求引數和請求引數值的對映關係。

下面是一段簡單的演示程式碼:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws Exception {
    System.out.println("getRequestURL: " + request.getRequestURL());
    System.out.println("getRequestURI: " + request.getRequestURI());
    System.out.println("getQueryString: " + request.getQueryString());
    System.out.println("getRemoteHost: " + request.getRemoteHost());
    System.out.println("getRemotePort: " + request.getRemotePort());
    System.out.println("getLocalAddr: " + request.getLocalAddr());
    System.out.println("getLocalName: " + request.getLocalName());
    System.out.println("getLocalPort: " + request.getLocalPort());
    System.out.println("getMethod: " + request.getMethod());
    System.out.println("-------request.getParamterMap()-------");
    // 得到請求的引數Map,注意map的value是String陣列型別
    Map map = request.getParameterMap();
    Set<String> keySet = map.keySet();
    for (String key : keySet) {
        String[] values = (String[]) map.get(key);
        for (String value : values) {
            System.out.println(key + "=" + value);
        }
    }
    System.out.println("--------request.getHeader()--------");
    // 得到請求頭的name集合
    Enumeration<String> em = request.getHeaderNames();
    while (em.hasMoreElements()) {
        String name = (String) em.nextElement();
        String value = request.getHeader(name);
        System.out.println(name + "=" + value);
    }
}
在瀏覽器位址列中存取下面的 URL:

http://127.0.0.1:8080/ServletDemo01/Servlet01?name=yanchangsheng&password=123456

?後面跟的是引數,多個引數之間用&連線。

控制台輸出結果:

getRequestURL: http://127.0.0.1:8080/ServletDemo01/Servlet01
getRequestURI: /ServletDemo01/Servlet01
getQueryString: name=yanchangsheng&password=123456
getRemoteHost: 127.0.0.1
getRemotePort: 57603
getLocalAddr: 127.0.0.1
getLocalName: 127.0.0.1
getLocalPort: 8080
getMethod: GET
-------request.getParamterMap()-------
name=yanchangsheng
password=123456
--------request.getHeader()--------
host=127.0.0.1:8080
connection=keep-alive
cache-control=max-age=0
upgrade-insecure-requests=1
user-agent=Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36
accept=text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
accept-encoding=gzip, deflate, br
accept-language=zh-CN,zh;q=0.9

HttpServletResponse 物件

與 HttpServletRequest 相反,HttpServletResponse 物件是專門用來封裝 HTTP 響應訊息的。用戶端發出了請求,得對人家有所回應呀,HttpServletResponse 就是做這項工作的。

由於 HTTP 響應訊息分為狀態行、響應訊息頭、響應訊息體三部分,因此,在 HttpServletResponse 介面中定義了向用戶端傳送響應狀態碼、響應訊息頭、響應訊息體的方法。

1) 傳送狀態碼相關的方法

① void setStatus(int status)
該方法用於設定 Http 響應訊息的狀態碼,並生成響應狀態行。

由於響應狀態行中的狀態描述資訊直接與狀態碼相關,而 HTTP 版本由伺服器確定,因此,只要通過 setStatus() 方法設定了狀態碼,即可實現狀態行的傳送。

需要注意的是,在正常情況下,Web 伺服器會預設產生一個狀態碼為 200 的狀態行。

② void sendError(int sc)
該方法用於傳送表示錯誤資訊的狀態碼。例如,404 狀態碼表示找不到用戶端請求的資源。

HttpServletResponse 物件提供了兩個過載的 sendError(int sc) 方法,具體如下:

public void sendError(int code) throws java.io.IOException
public void sendError(int code,String message)throws java.io.IOException

在上面過載的兩個方法中,第一個方法只傳送錯誤資訊的狀態碼,而第二個方法除了傳送狀態碼以外,還可以增加一條用於提示說明的文字資訊,該文字資訊將出現在傳送給用戶端的正文內容中。

2) 傳送響應訊息頭相關的方法

方法宣告 功能描述
void addHeader(String name,String value) 這兩個方法都是用來設定 HTTP 協定的響應頭欄位,其中:
  • 引數 name 用於指定響應頭欄位的名稱;
  • 引數 value 用於指定響應頭欄位的值。

不同的是,addHeader() 方法可以增加同名的響應頭欄位,而 setHeader() 方法則會覆蓋同名的頭欄位。
void setHeader(String name,String value)
void addIntHeader(String name,int value) 這兩個方法專門用於設定包含整數值的響應頭,避免了「使用 addHeader() 與 setHeader() 方法時需要將 int 型別的設定值轉換為 string 型別」的問題。
void setIntHeader(String name,int value)
void setContentLength(int len) 該方法用於設定響應訊息的實體內容的大小,單位為位元組。對於 HTTP 協定來說,這個方法就是設定 Content-Length 響應頭欄位的值。
void setContentType(String type) 該方法用於設定 Servlet 輸出內容的 MIME 型別。對於 HTTP 協定來說,就是設定 Content-Type 響應頭欄位的值。舉例說明:
  • 如果傳送到用戶端的內容是 jpeg 格式的影象資料,就需要將響應頭欄位的型別設定為image/jpeg
  • 如果響應的內容為文字,則該方法還可以設定字元編碼,例如text/html;charset=UTF-8
void setLocale(Locale loc) 該方法用於設定響應訊息的在地化訊息。對 HTTP 來說,就是設定 Content-Language 響頭欄位和 Content-Type 頭欄位中的字元集編碼部分。

需要注意的是,如果 HTTP 訊息沒有設定 Content-Type 頭欄位,setLocale() 方法設定的字元編碼則沒有效果,如果呼叫 setCharacterEncoding() 或 setContentType() 方法指定了響應頭內容的字元集編碼,則 setLocale() 方法將不再具有指定字元集編碼的功能。
void setCharacterEncoding(String charset) 該方法用於設定 out 輸出流中所採用的編碼。對 HTTP 協定來說,就是設定 Content-Type 頭欄位中的字元集編碼部分。如果沒有設定 Content-type 頭欄位,setCharacterEncoding() 方法設定的字元集編碼則沒有效果。

該方法的優先順序比setContentType()和setLocale()方法要高,它的設定結果將覆蓋setContentType()和setLocale()方法所設定的字元編碼。

3) 傳送響應訊息體相關的方法

① ServletOutputStream getOutputStream()方法
該方法所獲取的位元組輸出流物件為 ServletOutputStream 型別。

由於 ServletOutputStream 是 OutputStream 的子類,它可以直接輸出位元組陣列中的二進位制資料,因此,要想輸出二進位制格式的響應正文,就需要使用 getOutputStream() 方法。

② PrintWriter getWriter()方法
它返回了一個可以向用戶端傳送文字的的 Java.io.PrintWriter 物件。

由於 PrintWriter 型別的物件可以直接輸出字元文字內容,因此,要想輸出內容全為字元文字的網頁文件,需要使用 getWriter() 方法。

預設情況下,PrintWriter 物件使用 ISO-8859-1 編碼(該編碼在輸入中文時會發生亂碼,亂碼問題後面章節會講到)。

注意:雖然HttpServletResponse 物件的 getOutSream() 和 getWriter() 方法都可以傳送響應訊息體,但是他們之間相互排斥,不可以同時使用,否則會發生異常。