The Response
響應(yīng)對(duì)象封裝從服務(wù)器返回到客戶機(jī)的所有信息。在HTTP協(xié)議中,該信息通過HTTP頭或請(qǐng)求的消息體從服務(wù)器傳輸?shù)娇蛻魴C(jī)。
1.緩沖
servlet容器被允許,但不是必需的,以緩沖輸出到客戶端以達(dá)到效率目的。通常使用緩沖的服務(wù)器使其成為默認(rèn)的,但是允許servlet指定緩沖參數(shù)。
ServletResponse接口中的以下方法允許servlet訪問和設(shè)置緩沖信息:
- getBufferSize
- setBufferSize
- isCommitted
- reset
- resetBuffer
- flushBuffer
這些方法是在ServletResponse接口上提供的,以允許在servlet使用ServletOutputStream或Writer時(shí)執(zhí)行緩沖操作。
getBufferSize方法返回正在使用的底層緩沖區(qū)的大小。如果沒有使用緩沖,則該方法必須返回0的int值。
servlet可以通過使用setBufferSize方法請(qǐng)求首選緩沖區(qū)大小。所分配的緩沖區(qū)不需要是servlet所請(qǐng)求的大小,但必須至少與所請(qǐng)求的大小相同。這允許容器重用一組固定大小的緩沖區(qū),如果合適的話,可以提供比請(qǐng)求更大的緩沖區(qū)。在使用ServletOutputStream或Writer編寫任何內(nèi)容之前,必須調(diào)用該方法。如果已經(jīng)寫入了任何內(nèi)容或響應(yīng)對(duì)象,則該方法必須拋出IllegalStateException。
isCommitted方法返回一個(gè)布爾值,指示是否已將任何響應(yīng)字節(jié)返回給客戶端。flushBuffer方法強(qiáng)制將緩沖區(qū)中的內(nèi)容寫入客戶機(jī)。
當(dāng)響應(yīng)未提交時(shí),reset方法清除緩沖區(qū)中的數(shù)據(jù)。在重置調(diào)用之前,servlet所設(shè)置的標(biāo)題、狀態(tài)代碼和調(diào)用getWriter或getOutputStream的狀態(tài)也必須被清除。如果在沒有清除標(biāo)頭和狀態(tài)代碼的情況下響應(yīng)沒有提交,則resetBuffer方法將清除緩沖區(qū)中的內(nèi)容。
如果響應(yīng)已提交,并調(diào)用reset或resetBuffer方法,則必須拋出IllegalStateException。響應(yīng)及其相關(guān)緩沖區(qū)將保持不變。
當(dāng)使用緩沖區(qū)時(shí),容器必須立即將填充緩沖區(qū)的內(nèi)容刷新到客戶端。如果這是發(fā)送給客戶機(jī)的第一個(gè)數(shù)據(jù),則認(rèn)為響應(yīng)是提交的。
2. Headers
Servlet可以使用以下方法設(shè)置HTTP響應(yīng)頭:
- setHeader
- addHeader
setHeader方法用一個(gè)具有給定名稱和值設(shè)置頭。前面的header被新的header所代替。如果名稱對(duì)于存在了一組header值,則將這些值清除并替換為新值。
addHeader方法為帶有給定名稱的集合添加了一個(gè)header值。如果沒有與名稱關(guān)聯(lián)的標(biāo)頭,就會(huì)創(chuàng)建一個(gè)新集合。
header可能包含表示int或Date對(duì)象的數(shù)據(jù)。HttpServletResponse接口的以下便利方法允許servlet為適當(dāng)?shù)臄?shù)據(jù)類型設(shè)置正確的格式:
- setIntHeader
- setDateHeader
- addIntHeader
- addDateHeader
要成功地傳輸回客戶端,必須在提交響應(yīng)之前設(shè)置headers(而不是trailer)。在響應(yīng)提交后設(shè)置的頭(非掛載)將被servlet容器忽略。如果在RFC 7230中指定的HTTP trailer將在響應(yīng)中被發(fā)送,則必須使用HttpServletResponse的setTrailerFields()方法來提供。此方法必須在已寫入的chunked響應(yīng)中的最后一個(gè)塊之前調(diào)用。
Servlet程序員負(fù)責(zé)確保為Servlet生成的內(nèi)容在響應(yīng)對(duì)象中適當(dāng)?shù)卦O(shè)置content-type頭。HTTP 1.1規(guī)范不要求在HTTP響應(yīng)中設(shè)置這個(gè)頭。當(dāng)Servlet程序員不設(shè)置類型時(shí),Servlet容器不能設(shè)置默認(rèn)的內(nèi)容類型。
建議容器使用X-Powered-By HTTP頭來發(fā)布其實(shí)現(xiàn)信息。字段值應(yīng)該包含一個(gè)或多個(gè)實(shí)現(xiàn)類型,例如“Servlet/4.0”??蛇x地,容器和底層Java平臺(tái)的補(bǔ)充信息可以在括號(hào)內(nèi)的實(shí)現(xiàn)類型之后添加。容器應(yīng)可配置以抑制此報(bào)頭。
這里是此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報(bào)頭,它是在響應(yīng)主體之后出現(xiàn)的。trailer是在RFC 7230中指定的。它們?cè)诜謮K傳輸編碼和其他通信協(xié)議的實(shí)現(xiàn)中很有用。Servlet容器為trailer提供支持。
如果trailer頭已經(jīng)準(zhǔn)備好讀取,isTrailerFieldsReady()將返回true。然后,servlet可以通過HttpServletRequest接口的getTrailerFields()方法讀取HTTP請(qǐng)求的預(yù)告頭。
servlet可以通過為HttpServletResponse接口的setTrailerFields方法提供一個(gè)供應(yīng)商來編寫響應(yīng)??梢酝ㄟ^訪問HttpServletResponse接口的getTrailerFields()方法獲得trailer頭的供應(yīng)商。
關(guān)于規(guī)范規(guī)范的這兩種方法,請(qǐng)參見javadoc。
4.非阻塞IO
非阻塞IO只適用于servlet和過濾器中的異步請(qǐng)求處理(如第2.3.3.3節(jié)中定義的“異步處理”頁2-10)和升級(jí)處理(如第2.3.3.5節(jié)中定義的“升級(jí)處理”在第2-21頁)。否則,必須在ServletInputStream.setReadListener或ServletOutputStream.setWriteListener被調(diào)用時(shí)拋出IllegalStateException。為了支持Web容器中的非阻塞寫入,除了第3.7節(jié)中所描述的ServletRequest中所做的更改之外,在第3-28頁上“非阻塞IO”,還進(jìn)行了以下更改以處理響應(yīng)相關(guān)的類/接口。
WriteListener提供了以下回調(diào)方法,容器可以適當(dāng)?shù)卣{(diào)用這些方法。
- WriteListener
- void onWritePossible()
當(dāng)一個(gè)WriteListener注冊(cè)到ServletOutputStream時(shí),這個(gè)方法將在第一次有可能寫入數(shù)據(jù)時(shí)被容器調(diào)用。僅當(dāng)在ServletOutputStream上的isReady方法(描述如下)返回值為false之后容器才會(huì)調(diào)用onWritePossible方法,寫操作才有可能實(shí)現(xiàn)。 - onError(Throwable t)當(dāng)處理響應(yīng)發(fā)生錯(cuò)誤時(shí)調(diào)用。
- void onWritePossible()
- ServletOutputStream
- boolean isReady() 如果對(duì)ServletOutputStream將成功,則此方法返回true,否則將返回false。如果該方法返回true,則可以在ServletOutputStream上執(zhí)行寫操作。如果沒有進(jìn)一步的數(shù)據(jù)可以寫入ServletOutputStream,那么這個(gè)方法將返回false,直到底層數(shù)據(jù)被刷新,此時(shí)容器將調(diào)用WriteListener的onwriteable方法。隨后對(duì)該方法的調(diào)用將返回true。
- void setWriteListener(WriteListener listener).
將WriteListener與這個(gè)ServletOutputStream關(guān)聯(lián)起來。對(duì)于容器,當(dāng)可以寫入數(shù)據(jù)時(shí),調(diào)用WriteListener上的回調(diào)方法。注冊(cè)一個(gè)WriteListener將啟動(dòng)非阻塞IO。在那個(gè)時(shí)候切換到傳統(tǒng)的阻塞IO是違法的。在此非法切換到傳統(tǒng)的阻塞IO之后,使用IO相關(guān)的方法調(diào)用會(huì)產(chǎn)生不確定的行為。
Servlet容器必須以線程安全的方式訪問WriteListener中的方法。
5.便利方法
以下的便利方法存在于HttpServletResponse接口中:
- sendRedirect
- sendError
sendRedirect方法將設(shè)置適當(dāng)?shù)念^和內(nèi)容主體將客戶機(jī)重定向到不同的URL。通過相對(duì)URL路徑調(diào)用此方法是合法的,但是底層容器必須將相對(duì)路徑轉(zhuǎn)換為完全合格的URL,以便將其傳輸回客戶機(jī)。不管出于什么原因,如果部分URL不能轉(zhuǎn)化為有效的URL,那么這個(gè)方法必須拋出IllegalArgumentException。
sendError方法將為錯(cuò)誤消息設(shè)置適當(dāng)?shù)念^和內(nèi)容體,以返回給客戶機(jī)??梢韵騭endError方法提供一個(gè)可選的字符串參數(shù),該方法可用于錯(cuò)誤的內(nèi)容體中。
這些方法將具有提交響應(yīng)的副作用,如果它還沒有提交,就終止它。在調(diào)用這些方法之后,servlet不應(yīng)該對(duì)客戶機(jī)進(jìn)行進(jìn)一步的輸出。如果在調(diào)用這些方法之后,將數(shù)據(jù)寫入響應(yīng),則忽略數(shù)據(jù)。
如果將數(shù)據(jù)寫入響應(yīng)緩沖區(qū),但不返回給客戶機(jī)(即響應(yīng)未提交),則必須清除響應(yīng)緩沖區(qū)中的數(shù)據(jù),并使用這些方法所設(shè)置的數(shù)據(jù)替換。如果響應(yīng)被提交,這些方法必須拋出一個(gè)IllegalStateException。
6. 國際化
servlet應(yīng)該設(shè)置響應(yīng)的語言環(huán)境和字符編碼。區(qū)域設(shè)置使用ServletResponse.setLocale方法。該方法可重復(fù)調(diào)用;但在做出回應(yīng)后調(diào)用是無效的。如果servlet在提交頁面之前沒有設(shè)置語言環(huán)境,則使用容器的默認(rèn)語言環(huán)境來確定響應(yīng)的語言環(huán)境,但是沒有為與客戶機(jī)的通信(比如HTTP中的Content-Language頭)進(jìn)行規(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>
如果兩個(gè)元素都不存在或不提供映射,則setLocale使用一個(gè)容器依賴映射。可以反復(fù)調(diào)用setCharacterEncoding、setContentType和setLocale方法來更改字符編碼。在調(diào)用servlet響應(yīng)的getWriter方法后調(diào)用,或在響應(yīng)提交后調(diào)用,對(duì)字符編碼沒有影響。只有當(dāng)給定的內(nèi)容類型字符串為charset屬性提供值時(shí),對(duì)setContentType的調(diào)用才會(huì)設(shè)置字符編碼。調(diào)用setLocale設(shè)置了字符編碼僅當(dāng)setcharacter編碼或setContentType在之前沒有設(shè)置字符編碼。
如果servlet在調(diào)用ServletResponse接口的getWriter方法之前沒有指定字符編碼,或者響應(yīng)已提交,則使用默認(rèn)的ISO-8859-1。
如果使用的協(xié)議提供了這樣做的方法,容器必須與客戶端的語言環(huán)境和用于servlet響應(yīng)的寫入器的字符編碼通信。在HTTP的情況下,語言環(huán)境通過Content-Language頭進(jìn)行通信,字符編碼作為文本媒體類型的內(nèi)容類型頭的一部分。請(qǐng)注意,如果servlet沒有指定內(nèi)容類型,則無法通過HTTP報(bào)頭來傳遞字符編碼;然而,它仍然被用來編碼通過servlet響應(yīng)的writer寫的文本。
7.響應(yīng)對(duì)象的關(guān)閉
當(dāng)響應(yīng)被關(guān)閉時(shí),容器必須立即將響應(yīng)緩沖區(qū)中的所有剩余內(nèi)容刷新到客戶端。以下事件表明servlet滿足了請(qǐng)求,響應(yīng)對(duì)象將被關(guān)閉:
- servlet的service方法的結(jié)束。
- setContentLength中指定數(shù)量的內(nèi)容或setContentLengthLong方法回應(yīng)是大于零的且已經(jīng)被寫入響應(yīng)。
- sendError方法被調(diào)用。
- sendRedirect方法被調(diào)用。
- AsyncContext的complete方法被調(diào)用。
8.響應(yīng)對(duì)象的生存期
每個(gè)響應(yīng)對(duì)象只在servlet的service方法的范圍內(nèi)有效,或者在過濾器的doFilter方法的范圍內(nèi)有效,除非關(guān)聯(lián)的請(qǐng)求對(duì)象為組件啟用了異步處理。如果啟動(dòng)了相關(guān)請(qǐng)求的異步處理,則響應(yīng)對(duì)象在調(diào)用AsyncContext的complete方法之前仍然有效。容器通常會(huì)回收響應(yīng)對(duì)象,以避免響應(yīng)對(duì)象創(chuàng)建的性能開銷。開發(fā)人員必須意識(shí)到,在上述范圍之外維護(hù)對(duì)尚未調(diào)用相應(yīng)請(qǐng)求上的startAsync的響應(yīng)對(duì)象的引用可能會(huì)導(dǎo)致非確定性行為。