一、ServletRequest接口
HttpServletRequest接口繼承了ServletRequest接口,實現類通常代表一個實際的Http Request。
Servlet容器負責創建一個HttpServletRequest對象(在Web服務器接收到瀏覽器的一個請求后),然后Servlet容器把這個HttpServletRequest傳遞給相關Servlet的service方法。(其實同時創建和傳遞的還有HttpServletResponse對象,這也是我們在編寫相關Servlet時,無須關注request是如何被接收,也無須關注response最終如何被返回,這些都是Servlet容器和Web服務器的職責)
ServletRequest接口,定義了一個能代表實際request的對象,它包含了這個request的各項信息。
一個ServletRequest對象提供的數據包括了parameter name and value,attributes,以及input stream。HttpServletRequest對象還能提供額外的http協議相關的信息(他是唯一的子接口)。
當然接口只是定義了行為規范,Java為其提供了兩個基礎的實現,以供開發人員擴展:
HttpServletRequestWrapper, ServletRequestWrapper。
二、Request中的parameter 和 attributes
2.1 首先來個大體的區分
Request parameter是客戶端提交過來的參數,以“name=value”的字符串形式存儲在Request對象中,所以對於parameter,只有getParameter方法。
例如,我們登錄時提交賬戶信息,假如是GET方法提交(實際上不會用GET提交),那么在提交的信息會以"?username=root&password=123456"這種形式附加在表單指向的url后面,這種形式稱之為Query String。而POST方式則會把信息放在Request Body當中,這種形式稱之為POST Body。
一個有意思的點是,Query string data is presented(顯示) BEFORE post body data.
例如,如果使用Query String提交“a=hello”和POST Body提交“a=goodbye“和”a=world”,則結果參數集將被排序為a=(hello, goodbye, world)。
更新:發現我以前講得不夠清楚。可以參考下列網址:
How are parameters sent in an HTTP POST request?
HTTP中application/x-www-form-urlencoded字符說明
在將post表單數據填充到參數集(parameter set)之前,必須滿足以下條件:
- The request is an HTTP or HTTPS request.
- The HTTP method is POST.
- The content type is application/x-www-form-urlencoded.
- The servlet has made an initial call of any of the 'getParameter' family of methods on the request object.
如果不滿足條件,且post表單數據不包含在參數集中,post數據仍然可以通過request object的輸入流提供給servlet。
如果滿足這些條件,post表單數據將不再可以直接從請求對象的輸入流中讀取。
Request attributes則與客戶端無關,服務器端接收后產生的一個Request對象,可能在各個不同的Servlet間傳遞而進行必要的處理,這時我們可以在Servlet A中通過setAttribute(String name,Object)方法存儲一些信息,然后在Servlet B中通過getAttribute(String name)方法,取出相關的Object對象,進行一些處理。
API中提到了一點,Attributes are reset between requests,我有點疑惑為什么要特意提這樣一句,直觀上來說,在不同的Request對象上存儲東西,二者當然不會共享吧?所以對Attributes的存儲機制開始好奇了。
An attribute is a server variable that exists within a specified scope i.e.:
- application, available for the life of the entire application
- session, available for the life of the session
- request, only available for the life of the request
- page (JSP only), available for the current JSP page only
可以確定的一點是,當代碼中出現了setAttribute()方法時,那么這個Request對象必定會與一個RequestDispatcher對象一起工作了,因為你在此處添加了一些有用的數據,肯定要在別處取出來用吧?
2.2 RequestDispatcher接口
先總結,RequestDispatcher接口類用於request的轉發。
RequestDispatcher也是一個接口,它定義一個這樣的對象:接收來自客戶端的request,然后把他們送到服務器的其他資源上去(如servlet,html文件,JSP文件)。所以,通常是Servlet容器創建RequestDispatcher對象,用於包裝一個有特定路徑或者名稱的服務器端資源。
這個接口用於包裝servlet,但是servlet容器可以創建RequestDispatcher對象來包裝任何類型的資源。
在JDK中,這個接口沒有任何子接口和實現類。但是在ServletContext接口(Defines a set of methods that a servlet uses to communicate with its servlet container)中有幾個get方法來獲得RequestDispatcher對象。
RequestDispatcher接口,也僅有兩個方法:
void forward(ServletRequest request, ServletResponse response),把一個request從一個Servlet中轉發到服務端的另一處資源(servlet, JSP file, or HTML file)。
該方法允許一個servlet對請求進行初步處理,並允許另一個資源生成響應。
對於通過getRequestDispatcher()獲得的RequestDispatcher, ServletRequest對象調整了其路徑元素和參數,以匹配目標資源的路徑。
forward()方法,應該在將response提交給客戶機之前(在response body output被刷新之前)調用。如果response已經被提交了,調用forward()會拋出IllegalStateException。 response緩沖區中未提交的輸出,在forward()調用前,會自動被清除。(Uncommitted output in the response buffer is automatically cleared before the forward.)
傳遞給forward的兩個對象,要和servlet中service方法中獲得的對象是相同的。即針對的是同一次請求過程。
void include(ServletRequest request, ServletResponse response),這個方法用於向response中添加一些資源(servlet, JSP page, HTML file)。
這里就涉及到ServletResponse對象了,它代表着一個要被返回的Response。ServletResponse對象也由Servlet容器創建,(在這樣的一個轉發過程中)它的路徑元素和參數與調用者的保持不變(The ServletResponse object has its path elements and parameters remain unchanged from the caller's. )而且進行include操作的servlet,無法改變response的狀態碼,也無法設置header信息,這樣的操作會被無視掉。(why?)
這里 include方法的ServletRequest request和ServletResponse response必須與傳遞給調用該include方法的servlet的service方法的兩個ServletRequest request和ServletResponse response是同一對(反正一般也不會自己創建了),或者是包裝前面這兩個對象的ServletRequestWrapper或ServletResponseWrapper類的子類。
三、ServletResponse接口
3.1
HttpServletResponse接口的父接口,定義一個對象來幫助servlet向客戶端發送response。
Servlet容器會創建一個 ServletResponse對象,然后與ServletRequest一起傳遞給servlet的service方法。
如果要在MIME body response中發送二進制數據,請使用getOutputStream()返回的ServletOutputStream。(什么是MIME)
要發送字符數據,使用getWriter()返回的PrintWriter對象。
要混合二進制和文本數據,例如,創建一個multipart響應,請使用ServletOutputStream並手動管理字符部分。
MIME主體響應的字符集可以使用以下任何一種技術顯式指定:
per request, per web-app (using ServletContext.setRequestCharacterEncoding(java.lang.String), deployment descriptor(即web.xml)), and per container (for all web applications deployed in that container, using vendor specific configuration(更改web容器的配置)).
如果使用了前面提到的多種技術,那么優先級就是所列的順序。
對於每個請求,可以使用本接口中的setCharacterEncoding(java.lang.String)和setContentType(java.lang.String)方法顯式地指定響應的字符集,或者隱式地使用setLocale(java.util.Locale)方法 。
顯式規范優先於隱式規范。如果沒有明確指定字符集,將使用ISO-8859-1。
setCharacterEncoding、setContentType或setLocale方法的調用必須在getWriter之前,並在提交要使用的字符編碼的響應之前
3.2 方法
ServletResponse接口定義的方法並不復雜,都和設置response的相關的格式有關,其中的一些會設置reponse header。
四、再談談ServletContext接口
前面已經提到過了,ServletContext中定義了一系列方法讓servlet與servlet容器打交道,例如獲取文件的MIME類型、分派request或寫入日志文件。
每個Java虛擬機中的每個“web應用程序”,都有一個上下文(context)。
而一個“Web Application”的定義:
A "web application" is a collection of servlets and content installed under a specific subset of the server's URL namespace, such as /catalog and possibly installed via a .war file.)
對於在部署描述符中標記為“distributed”的web應用程序,每個虛擬機都有一個上下文實例。在這種情況下,上下文不能用作共享全局信息的位置(因為信息不是真正的全局的)。使用外部資源,比如數據庫。
ServletContext對象包含在ServletConfig對象中,這個對象用於Web服務器在初始化servlet時提供給servlet。API鏈接
ServletContext提供的方法,與我們在web.xml中常進行的一些配置相似。如添加Filter、Listener、Servlet等。