The Response? 響應
響應對象包裝了從服務器端返回到客戶端的所有信息。在HTTP協議上,這些信息既可以通過HTTP headers 又可以通過響應體從服務器端傳輸到客戶端。
5.1 緩沖
為了效率,servlet 容器允許但非必須緩沖到客戶端的輸出。典型地,服務器默認使用緩沖,但是允許 servlets 可以指明緩沖參數。
ServletResponse 接口中的下列方法允許 servlet 訪問和設置緩沖信息:
■ getBufferSize
■ setBufferSize
■ isCommitted?
■ reset
■ resetBuffer
■ flushBuffer
ServletResponse 接口提供的這些方法允許執行緩沖操作,無論 servlet 使用 ServletOutputStream 還是 Writer。
getBufferSize 方法返回被隱含使用的緩沖的大小。如果沒有緩沖被使用,這個方法返回整數值0。
servlet 可以使用 setBufferSize 方法請求一個最佳的緩沖大小。賦予的緩沖不必是 servlet 請求的大小,但是必須至少與與請求的大小一樣大。這樣允許容器重用一組固定大小的緩沖,如何合適的話,提供一個比請求更大的緩沖。該方法必須在使用 ServletOutputStream 或 Writer 寫入任何內容前調用。如果任何內容已經被寫入或響應對象已經被提交,此方法必須拋出一個IllegalStateException。
isCommitted 方法返回一個布爾值表示所有(任何)響應字節已經返回到客戶端。
flushBuffer 方法強制緩沖中的內容被寫到客戶端。
當響應沒有被提交時,reset 方法清除緩沖中的數據。Headers,status codes 和 調用 reset 之前調用 getWriter 或 getOutputStream 被 servlet 設置的狀態也被清除。如果響應沒有被提交,resetBuffer 方法清除緩沖中的內容,但是不清除 headers 和status code。
如果響應已經被提交,調用 reset 或者 resetBuffer 方法,必須拋出IllegalStateException。響應和它關聯的緩沖將不會改變。
當使用緩沖時,容器必須立即將填滿緩沖的內容刷出到客戶端 。如果這是發送到客戶端的第一(唯一?)數據,可認為響應已經被提交。
5.2 Headers 頭
servlet 可以通過 HttpServletResponse 接口的以下方法設置 HTTP 響應的頭:
■ setHeader
■ addHeader
setHeader方法使用一個給定的名稱和值設置一個 header。之前的 header 被新 header 替換。當此名有一個 header 值集合,所有值被清除并用新值代替。
addHeader 方法添加一個 header 值到給定名稱的頭的值集。如果該名稱沒有關聯的頭,一個新集被創建。Headers 可以包含表示 int 或 Date 對象的數據。HttpServletResponse 接口的下列便捷方法允許 servlet 為合適的數據類型使用正確的格式設置 header:
■ setIntHeader
■ setDateHeader
■ addIntHeader
■ addDateHeader
為了成功的傳回客戶端,頭必須在響應提交之前設置。響應提交之后設置的頭將被 servlet 容器忽略。
Servlet 開發者有責任保證為 servlet 生成的內容在響應對象中設置合適的 Content-type 頭。HTTP 1.1 規范沒有要求 HTTP 響應中設置此頭。開發者沒有設置此類型時,servlet 容器不必設置一個默認的內容類型(content type)。
建議容器使用 X-Powered-By HTTP header 發布它的實現信息。這個字段的值應該由一個或者多個實現類型組成,比如:”Servlet/3.1”。可選的,容器的補充信息和隱含的Java平臺信息可以添加在實現類型后面的括號內。容器應可配置隱藏此 header。
以下是此 header 的例子:
X-Powered-By: Servlet/3.1
X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition 4.0 Java/Oracle Corporation/1.7)
5.3 Non Blocking IO ?非阻塞IO
非阻塞IO只有在 Servlets 和 Filters 的異步請求處理和升級處理時才有效。否則當調用 ServletInputStream.setReadListener 或 ServletOutputStream.setWriteListener 時必須拋出IllegalStateException。為了支持在Web容器中非阻塞寫,除了在3.7節中描述的 ServletRequest 的變化之外,與處理響應有關的類/接口也發生了下面的變化:
WriteListener 提供了適合容器調用的如下回調方法:
■ WriteListener
■ void onWritePossible(). 當一個 WriteListener 注冊到 ServletOutputStream 時,當可以寫數據時這個方法被容器第一次調用。當且僅當下面描述的 ServletOutputStream 的 isReady 方法返回false 時,容器接下來會調用 onWritePossible 方法。
■ onError(Throwable t). 當處理響應發生錯誤時被調用。
與WriteListener一起,下面添加到 ServletOutputStream 類的方法允許開發者在運行時檢查是否有發送到客戶端的可寫數據。
■ ServletOutputStream
- boolean isReady(). 如果對 ServletOutputStream 的寫會成功,該方法返回true,否則返回 false。如果該方法返回 true,可以在 ServletOutputStream 上執行一個寫操作。如果沒有后續數據可以寫到 ServletOutputStream,那么直到容器調用 WriteListener 的 onWritePossible 方法將隱含的數據刷出之前,此方法將返回false。該方法的后續調用將會返回true。
- void setWriteListener(WriteListener listener). 用 WriteListener 關聯 ServletOutputStream。當 ServletOutputStream 可寫數據時,容器調用 WriteListener 上的回調方法。注冊一個 WriteListener 將開啟非阻塞IO。此時切換到傳統阻塞IO是非法的。Servlet 容器必須用線程安全的方式訪問 WriteListener 的方法。?
5.4 Convenience Methods 便捷方法?
?HttpServletResponse 接口中存在以下便捷方法:
■ sendRedirect
■ sendError
sendRedirect方法將設置合適的頭和內容體重定向客戶端到一個不同的URL。調用這個方法時使用一個相對URL路徑是合法的,但是隱含的容器必須轉換這個相對路徑為一個全路徑URL傳回客戶端。如果給了一個不完整的URL,不管什么原因都不能轉換為一個有效的URL,此方法必須拋出一個IllegalArgumentException。
sendError方法將為錯誤信息設置合適的頭和內容體返回到客戶端。sendError方法支持一個可選的字符串參數用來設置錯誤的內容體。
這些方法會對提交中的響應產生副作用,如果響應還沒有被提交,將會中斷它(?)。這些方法被調用之后,servlet 不會產生到客戶端的后續輸出。如果這些方法被調用后,有數據寫到了響應,這些數據將被忽略。如果數據被寫到了響應緩沖,但是還沒有返回到客戶端(比如:響應還沒有比提交),響應緩沖中的數據必須被清除,并且用這些方法設置的數據來替代。如果響應被提交,這些方法必須拋出一個IllegalStateException。
?
5.5 Internationalization? 國際化
Servlets 應該設置 locale 和響應的字符編碼。locale 使用 ServletResponse.setLocale 方法設置。該方法可以重復調用;但是響應提交后的調用沒有效果。如果頁面提交前 servlet 沒有設置 locale,使用容器的默認 locale 來確定響應的 locale,但是沒有為與客戶端的通信制定規范,比如使用 HTTP 情況下的Content-Language 頭。
<locale-encoding-mapping-list>
??? <locale-encoding-mapping>
??????? <locale>ja</locale>
??????? <encoding>Shift_JIS</encoding>
??? </locale-encoding-mapping>
</locale-encoding-mapping-list>
如果元素不存在或者沒有提供一個映射,setLocale 使用容器依賴的映射。setCharacterEncoding ,setContentType和 setLocale 方法可以被重復調用來改變字符編碼。在 servlet 響應的 getWriter 方法被調用后或響應被提交后的調用不會對字符編碼產生影響。僅在用給定的內容類型字符串為 charset 屬性提供一個值的時候,調用 setContentType 來設置字符編碼。只有當既沒有使用 setCharacterEncoding 又沒有使用 setContentType 設置字符編碼之前調用 setLocale 設置字符編碼(是不是應該設置Locale?)。
如果調用 ServletResponse 接口的 getWriter 方法或響應提交之前,servlet 沒有指定字符編碼,將默認使用“ISO-8859-1”。
如果使用的協議提供了這樣的方法,容器必須將 servlet 響應的 wirter 使用的 locale 和字符編碼傳給客戶端。在使用HTTP時,通過 Content-Language 頭傳遞 locale,字符編碼作為文本媒體類型 Content-Type 頭的一部分傳遞。注意:如果 servlet 沒有指明內容類型(content type),字符編碼不能通過HTTP 頭傳遞,但是仍然可以使用 servlet 響應的 writer 對文本編碼。
?
5.6 ?Closure of Response Object? 響應對象的關閉?
???????? 當響應關閉時,容器必須立即將響應緩沖中剩余的內容刷出到客戶端。下面的事件表明 servlet 滿足了請求,響應對象將被關閉:
- servlet 的 service 方法結束。?
- 在 response 的 setContentLength 或 setContentLengthLong 方法中指定的內容量大于 0,并且已經寫入響應。?
- sendError 方法被調用。?
- sendRedirect 方法被調用。?
- AsyncContext 的 complete 方法被調用。?
5.7??? ?Lifetime of the Response Object? 響應對象的生命周期
除非關聯的請求對象為組件開啟了異步處理,否則每個響應對象只在 servlet 的 service 方法或 filter 的 doFilter 方法的作用域中有效。如果相關聯請求的異步處理已經開始,在 AsyncContext 的 complete 方法調用之前該請求對象一直有效。為了避免創建響應對象的性能開銷,容器通常會回收利用響應對象。開發者必須注意相應的請求沒有被調用 startAsync 方法,在上面描述的作用域之外保持響應對象的引用可能會導致無法確定的行為。?