當請求來到伺服器時,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)之類的事情發生,永遠別把文件或書中簡單的範例程式直接拿來用,為了簡化範例,程式中往往不會考慮必要的驗證與過濾!