當請求來到伺服器時,Web 容器會建立 HttpServletRequest 實例來包裝請求中的相關訊息, HttpServletRequest 定義了取得一些通用請求資訊的方法,例如請求參數,可以使用以下的方法來取得:
- getParameter()- 指定請求參數名稱來取得對應的值,例如: - String username = request.getParameter("name");- getParameter()傳回的是- String物件,若傳來的是像- "123"這樣的字串值,而你需要的是基本資料型態,則必須使用- Integer.parseInt()這類的方法將之剖析為基本型態。若請求中沒有所指定的請求參數名稱,則會傳回- null。
- getParameterValues()- 如果表單上有可複選的元件,例如核取方塊、多選清單等,則同一個請求參數名稱會有多個值(此時的 HTTP 查詢字串其實就是像 param=10¶m=20¶m=30), 此時你可以用 - getParameterValues()方法取得一個- String陣列,陣列元素代表所有被選取選項的值。例如:- String[] values = request.getParameterValues("param");
- getParameterNames()- 如果想要知道請求中有多少請求參數,則可以使用 - getParameterNames()方法,這會傳回一個- Enumeration物件,當中包括所有的請求參數名稱。例如:- Enumeration<String> e = request.getParameterNames(); while(e.hasMoreElements()) { String param = e.nextElement(); String value = request.getParameter(param); out.println(value); }- Enumeration是從 JDK1.0 就存在的 API,如果你想使用增強式- for迴圈,或者是 Java SE 8 新增 Stream API 的話,可以使用- java.util.Collections的靜態方法 list(),將元素收集至- List實例之中,例如:- Collections.list(request.getParameterNames()) .stream() .map(request::getParameter) .forEach(out::println);
- getParameterMap()- 將請求參數以 - Map物件傳回,- Map中的鍵(Key)是請求參數名稱,值(Value)的部份是請求參數值,以字串陣列型態- String[]傳回(考慮有同一請求參數有多個值的情況)。
對於 HTTP 的標頭(Header)資訊,可以使用以下的方法來取得:
- getHeader()- 使用方式與 - getParameter()類似,指定標頭名稱後可傳回字串值,代表瀏覽器所送出的標頭訊息。
- getHeaders()- 使用方式與 - getParameterValues()類似,指定標頭名稱後可傳回- Enumeration,元素為字串。
- getHeaderNames()- 使用方式與 - getParameterNames()類似,取得所有標頭名稱,以- Enumeration傳回,內含所有標頭字串名稱。
下面這個範例示範了如何取得並顯示瀏覽器送出的標頭訊息:
package cc.openhome;
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;
@WebServlet("/headers")
public class Headers extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        PrintWriter out = response.getWriter();
        out.println("<!DOCTYPE html>");
        out.println("<html>");
        out.println("<head>");
        out.println("<title>Servlet ShowHeader</title>");
        out.println("</head>");
        out.println("<body>");
        Collections.list(request.getHeaderNames())
                   .forEach(name -> {
                       out.printf("%s: %s<br>%n", name, request.getHeader(name));
                   });
        out.println("</body>");
        out.println("</html>");
        out.close();
    }
}
如果標頭值本身是個整數或日期的字串表示法,則可以使用 getIntHeader() 或 getDateHeader() 方法分別取得轉換為 int 或 Date 的值。如果 getIntHeader() 無法轉換為 int,則會丟出 NumberFormatException,如果 getDateHeader() 無法轉換為 Date,則會丟出 IllegalArgumentException。
若請求參數是放在請求本體之中,例如 POST,而客戶端沒有在 Content-Type 標頭中設定字元編碼資訊(例如 Content-Type: text/html; charset=UTF-8),此時使用 HttpServletRequest 的 getCharacterEncoding() 傳回值會是 null,此時容器使用的預設編碼處理若是 ISO-8859-1(例如 Tomcat),而客戶端使用 UTF-8 發送非 ASCII 字元的請求參數,Servlet 取得的請求參數值,就會是不正確的結果(亂碼)。
HttpServletRequest 是有個 setCharacterEncoding() 方法,可以指定取得請求參數時所使用的編碼,這個方法必須在取得任何請求值之前執行:
request.setCharacterEncoding("UTF-8");
不過在規格書中對 setCharacterEncoding() 清楚提到其作用為:
Overrides the name of the character encoding used in the body of this request.
也就是說,基本上這個方法只對請求參數放在本體中時有用(例如 POST),就方才的程式片段來說,若瀏覽器以 UTF-8 來發送請求,取得的請求參數值就會是正確的結果。
從 Servlet 4.0 開始,也可以在 web.xml 中加入 <request-character-encoding>,設定整個應用程式要使用的請求參數編碼,如此一來,就不用特別在每次請求使用 HttpServletRequest 的 setCharacterEncoding() 方法來設定編碼了 例如:
<request-character-encoding>UTF-8</request-character-encoding>
當請求是用 GET 發送時,setCharacterEncoding() 沒有作用,究其原因,是因為處理 URI 的是 HTTP 伺服器,而非容器,對於 Tomcat 7 或先前版本附帶的 HTTP 伺服器來說,處理 URI 時使用的預設編碼是 ISO-8859-1,在不改變 Tomcat 附帶的 HTTP 伺服器 URI 編碼處理設定的情況下,常見使用底下的處理方式(若客戶端使用 UTF-8 發送請求):
String name = request.getParameter("name");
String name = new String(name.getBytes("ISO-8859-1"), "UTF-8");
從 Tomcat 8 之後,附帶的 HTTP 伺服器在 URI 編碼處理時預設使用 UTF-8 了,此時就不用如上自行轉換字串編碼了。
一旦開始學會從客戶端接受請求,取得請求參數或標頭等客戶端送出的資料之後,切記,永遠別相信你的客戶端是善良的,小心驗證、過濾請求參數或標頭等,以避免注入攻擊(Injection attack)之類的事情發生,永遠別把文件或書中簡單的範例程式直接拿來用,為了簡化範例,程式中往往不會考慮必要的驗證與過濾!

