The Response
響應對象封裝從服務器返回到客戶機的所有信息。在HTTP協(xié)議中赖临,該信息通過HTTP頭或請求的消息體從服務器傳輸?shù)娇蛻魴C胞锰。
1.緩沖
servlet容器被允許,但不是必需的兢榨,以緩沖輸出到客戶端以達到效率目的嗅榕。通常使用緩沖的服務器使其成為默認的,但是允許servlet指定緩沖參數(shù)吵聪。
ServletResponse接口中的以下方法允許servlet訪問和設置緩沖信息:
- getBufferSize
- setBufferSize
- isCommitted
- reset
- resetBuffer
- flushBuffer
這些方法是在ServletResponse接口上提供的凌那,以允許在servlet使用ServletOutputStream或Writer時執(zhí)行緩沖操作。
getBufferSize
方法返回正在使用的底層緩沖區(qū)的大小吟逝。如果沒有使用緩沖帽蝶,則該方法必須返回0的int值。
servlet可以通過使用setBufferSize
方法請求首選緩沖區(qū)大小块攒。所分配的緩沖區(qū)不需要是servlet所請求的大小励稳,但必須至少與所請求的大小相同。這允許容器重用一組固定大小的緩沖區(qū)囱井,如果合適的話驹尼,可以提供比請求更大的緩沖區(qū)。在使用ServletOutputStream或Writer編寫任何內容之前庞呕,必須調用該方法新翎。如果已經寫入了任何內容或響應對象,則該方法必須拋出IllegalStateException住练。
isCommitted方法返回一個布爾值地啰,指示是否已將任何響應字節(jié)返回給客戶端。flushBuffer方法強制將緩沖區(qū)中的內容寫入客戶機澎羞。
當響應未提交時髓绽,reset方法清除緩沖區(qū)中的數(shù)據(jù)敛苇。在重置調用之前妆绞,servlet所設置的標題、狀態(tài)代碼和調用getWriter或getOutputStream的狀態(tài)也必須被清除枫攀。如果在沒有清除標頭和狀態(tài)代碼的情況下響應沒有提交括饶,則resetBuffer方法將清除緩沖區(qū)中的內容。
如果響應已提交来涨,并調用reset或resetBuffer方法图焰,則必須拋出IllegalStateException。響應及其相關緩沖區(qū)將保持不變蹦掐。
當使用緩沖區(qū)時技羔,容器必須立即將填充緩沖區(qū)的內容刷新到客戶端僵闯。如果這是發(fā)送給客戶機的第一個數(shù)據(jù),則認為響應是提交的藤滥。
2. Headers
Servlet可以使用以下方法設置HTTP響應頭:
- setHeader
- addHeader
setHeader方法用一個具有給定名稱和值設置頭鳖粟。前面的header被新的header所代替。如果名稱對于存在了一組header值拙绊,則將這些值清除并替換為新值向图。
addHeader方法為帶有給定名稱的集合添加了一個header值。如果沒有與名稱關聯(lián)的標頭标沪,就會創(chuàng)建一個新集合榄攀。
header可能包含表示int或Date對象的數(shù)據(jù)。HttpServletResponse接口的以下便利方法允許servlet為適當?shù)臄?shù)據(jù)類型設置正確的格式:
- setIntHeader
- setDateHeader
- addIntHeader
- addDateHeader
要成功地傳輸回客戶端金句,必須在提交響應之前設置headers(而不是trailer)檩赢。在響應提交后設置的頭(非掛載)將被servlet容器忽略。如果在RFC 7230中指定的HTTP trailer將在響應中被發(fā)送违寞,則必須使用HttpServletResponse的setTrailerFields()方法來提供漠畜。此方法必須在已寫入的chunked響應中的最后一個塊之前調用。
Servlet程序員負責確保為Servlet生成的內容在響應對象中適當?shù)卦O置content-type頭坞靶。HTTP 1.1規(guī)范不要求在HTTP響應中設置這個頭憔狞。當Servlet程序員不設置類型時,Servlet容器不能設置默認的內容類型彰阴。
建議容器使用X-Powered-By HTTP頭來發(fā)布其實現(xiàn)信息瘾敢。字段值應該包含一個或多個實現(xiàn)類型,例如“Servlet/4.0”尿这〈氐郑可選地,容器和底層Java平臺的補充信息可以在括號內的實現(xiàn)類型之后添加射众。容器應可配置以抑制此報頭碟摆。
這里是此header的示例:
X-Powered-By: Servlet/4.0
X-Powered-By: Servlet/4.0 JSP/2.3 (GlassFish Server Open Source
Edition 5.0 Java/Oracle Corporation/1.8)
3.HTTP Trailer
HTTP trailer是一種特殊類型的HTTP報頭,它是在響應主體之后出現(xiàn)的叨橱。trailer是在RFC 7230中指定的典蜕。它們在分塊傳輸編碼和其他通信協(xié)議的實現(xiàn)中很有用。Servlet容器為trailer提供支持罗洗。
如果trailer頭已經準備好讀取愉舔,isTrailerFieldsReady()將返回true。然后伙菜,servlet可以通過HttpServletRequest接口的getTrailerFields()方法讀取HTTP請求的預告頭轩缤。
servlet可以通過為HttpServletResponse接口的setTrailerFields方法提供一個供應商來編寫響應。可以通過訪問HttpServletResponse接口的getTrailerFields()方法獲得trailer頭的供應商火的。
關于規(guī)范規(guī)范的這兩種方法壶愤,請參見javadoc。
4.非阻塞IO
非阻塞IO只適用于servlet和過濾器中的異步請求處理(如第2.3.3.3節(jié)中定義的“異步處理”頁2-10)和升級處理(如第2.3.3.5節(jié)中定義的“升級處理”在第2-21頁)馏鹤。否則公你,必須在ServletInputStream.setReadListener或ServletOutputStream.setWriteListener被調用時拋出IllegalStateException。為了支持Web容器中的非阻塞寫入假瞬,除了第3.7節(jié)中所描述的ServletRequest中所做的更改之外陕靠,在第3-28頁上“非阻塞IO”,還進行了以下更改以處理響應相關的類/接口脱茉。
WriteListener提供了以下回調方法剪芥,容器可以適當?shù)卣{用這些方法。
- WriteListener
- void onWritePossible()
當一個WriteListener注冊到ServletOutputStream時琴许,這個方法將在第一次有可能寫入數(shù)據(jù)時被容器調用税肪。僅當在ServletOutputStream上的isReady方法(描述如下)返回值為false之后容器才會調用onWritePossible方法,寫操作才有可能實現(xiàn)榜田。 - onError(Throwable t)當處理響應發(fā)生錯誤時調用益兄。
- void onWritePossible()
- ServletOutputStream
- boolean isReady() 如果對ServletOutputStream將成功,則此方法返回true箭券,否則將返回false净捅。如果該方法返回true,則可以在ServletOutputStream上執(zhí)行寫操作辩块。如果沒有進一步的數(shù)據(jù)可以寫入ServletOutputStream蛔六,那么這個方法將返回false,直到底層數(shù)據(jù)被刷新废亭,此時容器將調用WriteListener的onwriteable方法国章。隨后對該方法的調用將返回true。
- void setWriteListener(WriteListener listener).
將WriteListener與這個ServletOutputStream關聯(lián)起來豆村。對于容器液兽,當可以寫入數(shù)據(jù)時,調用WriteListener上的回調方法掌动。注冊一個WriteListener將啟動非阻塞IO四啰。在那個時候切換到傳統(tǒng)的阻塞IO是違法的。在此非法切換到傳統(tǒng)的阻塞IO之后坏匪,使用IO相關的方法調用會產生不確定的行為拟逮。
Servlet容器必須以線程安全的方式訪問WriteListener中的方法。
5.便利方法
以下的便利方法存在于HttpServletResponse接口中:
- sendRedirect
- sendError
sendRedirect方法將設置適當?shù)念^和內容主體將客戶機重定向到不同的URL适滓。通過相對URL路徑調用此方法是合法的,但是底層容器必須將相對路徑轉換為完全合格的URL恋追,以便將其傳輸回客戶機凭迹。不管出于什么原因,如果部分URL不能轉化為有效的URL,那么這個方法必須拋出IllegalArgumentException罚屋。
sendError方法將為錯誤消息設置適當?shù)念^和內容體,以返回給客戶機嗅绸∑⒚停可以向sendError方法提供一個可選的字符串參數(shù),該方法可用于錯誤的內容體中鱼鸠。
這些方法將具有提交響應的副作用猛拴,如果它還沒有提交,就終止它蚀狰。在調用這些方法之后愉昆,servlet不應該對客戶機進行進一步的輸出。如果在調用這些方法之后麻蹋,將數(shù)據(jù)寫入響應跛溉,則忽略數(shù)據(jù)。
如果將數(shù)據(jù)寫入響應緩沖區(qū)扮授,但不返回給客戶機(即響應未提交)芳室,則必須清除響應緩沖區(qū)中的數(shù)據(jù),并使用這些方法所設置的數(shù)據(jù)替換刹勃。如果響應被提交堪侯,這些方法必須拋出一個IllegalStateException。
6. 國際化
servlet應該設置響應的語言環(huán)境和字符編碼荔仁。區(qū)域設置使用ServletResponse.setLocale
方法抖格。該方法可重復調用;但在做出回應后調用是無效的。如果servlet在提交頁面之前沒有設置語言環(huán)境咕晋,則使用容器的默認語言環(huán)境來確定響應的語言環(huán)境雹拄,但是沒有為與客戶機的通信(比如HTTP中的Content-Language頭)進行規(guī)范。
<locale-encoding-mapping-list>
<locale-encoding-mapping>
<locale>ja</locale>
<encoding>Shift_JIS</encoding>
</locale-encoding-mapping>
</locale-encoding-mapping-list
The <response-character-encoding> element can be used to explicitly set the default encoding for all responses in a given web application.
<response-character-encoding>UTF-8</response-character-encoding>
如果兩個元素都不存在或不提供映射掌呜,則setLocale使用一個容器依賴映射滓玖。可以反復調用setCharacterEncoding质蕉、setContentType和setLocale方法來更改字符編碼势篡。在調用servlet響應的getWriter方法后調用,或在響應提交后調用模暗,對字符編碼沒有影響禁悠。只有當給定的內容類型字符串為charset屬性提供值時,對setContentType的調用才會設置字符編碼兑宇。調用setLocale設置了字符編碼僅當setcharacter編碼或setContentType在之前沒有設置字符編碼碍侦。
如果servlet在調用ServletResponse接口的getWriter方法之前沒有指定字符編碼,或者響應已提交,則使用默認的ISO-8859-1瓷产。
如果使用的協(xié)議提供了這樣做的方法站玄,容器必須與客戶端的語言環(huán)境和用于servlet響應的寫入器的字符編碼通信。在HTTP的情況下濒旦,語言環(huán)境通過Content-Language頭進行通信株旷,字符編碼作為文本媒體類型的內容類型頭的一部分。請注意尔邓,如果servlet沒有指定內容類型晾剖,則無法通過HTTP報頭來傳遞字符編碼;然而,它仍然被用來編碼通過servlet響應的writer寫的文本梯嗽。
7.響應對象的關閉
當響應被關閉時齿尽,容器必須立即將響應緩沖區(qū)中的所有剩余內容刷新到客戶端。以下事件表明servlet滿足了請求慷荔,響應對象將被關閉:
- servlet的service方法的結束雕什。
- setContentLength中指定數(shù)量的內容或setContentLengthLong方法回應是大于零的且已經被寫入響應。
- sendError方法被調用显晶。
- sendRedirect方法被調用贷岸。
- AsyncContext的complete方法被調用。
8.響應對象的生存期
每個響應對象只在servlet的service方法的范圍內有效磷雇,或者在過濾器的doFilter方法的范圍內有效偿警,除非關聯(lián)的請求對象為組件啟用了異步處理。如果啟動了相關請求的異步處理唯笙,則響應對象在調用AsyncContext的complete方法之前仍然有效螟蒸。容器通常會回收響應對象,以避免響應對象創(chuàng)建的性能開銷崩掘。開發(fā)人員必須意識到七嫌,在上述范圍之外維護對尚未調用相應請求上的startAsync的響應對象的引用可能會導致非確定性行為。