Servlet基础之HttpServletResponse详解
在上一文中,我們詳細的介紹了HttpServletRequest對象,并且重點的介紹一個Http請求中包含請求行、請求頭、請求體三部分,并且講解了如何通過HttpServletRequest中封裝的方法來方便的獲取對應的頭信息及用戶的請求參數。
? 在上文中我們也講到了Servlet的功能和重要性,其在MVC架構中充當Controller的角色,因此其不僅要獲取客戶端的數據(用戶輸入的表單數據、查詢參數等),并在處理結束后,給客戶端一個響應,也正如我們上圖中所示,Servlet需要對客戶端的HTTP請求進行一個HTTP響應。而Http的響應正是由本文中的主角HttpServletResponse來完成的,下面就讓我們一起學習如何對一個Http請求作出正確(適當的,處理成功或失敗、無權限、參數錯誤等)的響應。其局部(省去了Servlet容器)的執行過程如下圖所示:
1.設置響應的狀態碼
? 這里我們通過一個截圖來看下什么是響應的狀態碼,滴、滴、滴(😅,用圖就用全套,此部分圖片來自上篇文章),下圖最大的紅框中的綠色小燈泡旁的200字樣,就是我們這里說的狀態碼了,綠燈表示其為響應成功。
? 有些同學可能會比較疑惑了,我在開發的過程中并沒有設置response的狀態碼為200呀,怎么這里的200是哪來的?這里需要給大家說一下了,在我們程序正常執行(完整的處理了Http請求,沒發生bug)的時候,Web服務器會默認產生一個狀態碼為200。還有,在我們url路徑輸錯的時返回的404錯誤,調用servelt發生異常直接拋出返回的500錯誤等,都是web服務器幫我們默認產生的。
?
? 在開發的過程中,500錯誤是我們遇到最多的錯誤了,如空指針異常、sql異常、狀態異常等導致的請求中斷,對于這些異常,如果直接將報錯信息暴露給用戶,那么我們的系統的體驗就會非常的差。為了增加系統的用戶友好程序,我們必須對異常進行處理,但是也需要將錯誤信息正確的提示給用戶,讓其可以有下一步處理或者聯系客服。
? 這樣我們就需要來設置狀態碼,讓前端可以根據狀態碼及其他返回信息進行相應的頁面處理了。那么問題來了,我們如何手動的設置狀態碼呢?HttpServletResponse中為我們提供了一下幾個方法:
? 其中比較重要的方法為setStatus(int sc),我們可以通過其方便的設置給客戶端響應的狀態碼;對于sendError()的兩個方法,會將Servlet之前寫入緩沖區的數據全部清除,但是其也有較好的使用場景,就是對同一種異常設置專門的錯誤提示頁面,比如用戶未登錄,可以在過濾器(Filter)中判斷出此種情況,并直接調用sendError跳轉至相應的頁面,在此頁面上友好的向用戶提示錯誤信息,but此功能完全可以使用重定向來完成,且重定向可以獲取更多的請求和響應信息,因此算是一個比較雞肋的功能吧。
? 我們簡單的對setStatus、sendError進行測試,這里我們新建個Servelt,命名為ResponseTestServlet,其doGet代碼如下:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ? ? ? ? ? ??? ??? ??? ??? ?ServletException, IOException {
? //設置返回客戶端的contentType
? response.setContentType("text/html;charset=utf-8");
? //設置狀態碼
? response.setStatus(500);
? //response.sendError(500);
? //獲取輸出流
? PrintWriter out = response.getWriter();
? out.println("雖然我的狀態為500,但是信息正常輸出了");
}
1
2
3
4
5
6
7
8
9
10
? 我們直接在瀏覽器中調用ResponseTestServlet,其運行結果如下圖圖左所示:
?
? 當我們執行response.sendError(500)時,我覺得大家應該都已經預料到結果頁面了,其結果如上圖圖右所示。那我們應該如何設置錯誤碼對應的錯誤頁面呢?這里我們需要在web.xml中增加如下配置,需要注意的是,配置的路徑必須以’/'開頭,即必須是絕對路徑===打包發布時的路徑(可參考此文):
<error-page>
? <error-code>500</error-code>
? <location>/index.jsp</location>
</error-page>
1
2
3
4
? 在此在瀏覽器上執行,其運行結果如下圖所示(偷個懶,沒有寫錯誤顯示頁面😅):
? 如何根據錯誤信息,設置合理的狀態碼呢?這就需要我們知道每個狀態碼表示的含義。Http錯誤碼總共分為5類,即1xx、2xx、3xx、4xx、5xx,分別表示通知信息、成功信息、重定向信息、客戶端錯誤、服務端錯誤,下面列舉一些常見的錯誤碼:
Name?? ?discribtion?? ?釋義
200?? ?SC_OK?? ?此次請求已經成功
301?? ?SC_MOVED_PERMANENTLY?? ?請求的網頁已永久移動到新位置
302?? ?SC_MOVED_TEMPORARILY?? ?臨時移動、請求地址不變
401?? ?SC_UNAUTHORIZED?? ?未授權、用戶需登錄
403?? ?SC_FORBIDDEN?? ?服務器拒絕了此次請求(權限問題)
404?? ?SC_NOT_FOUND?? ?服務器沒找到URI匹配的
405?? ?SC_METHOD_NOT_ALLOWED?? ?調用的方法不允許使用(get、post不匹配)
500?? ?SC_INTERNAL_SERVER_ERROR?? ?服務器內部發生異常,請求中斷
502?? ?SC_BAD_GATEWAY?? ?網關錯誤(如Nginx),無法收到服務器的響應
504?? ?SC_GATEWAY_TIMEOUT?? ?請求超時,在約定時間內沒有收到Http響應
2.設置響應消息頭
? 在上文中,我們在chrom調試工具中查看了Http請求的請求頭、請求體,Chrome提供的信息不止于此,我們來看下圖,可以看到,Response的Headers信息。
? 這也說明了,相應于Request中的請求頭,Response也有對應的響應頭,這些響應頭主要如下圖所示:
? 當然,為了方便的設置響應頭中對應的信息,HttpServletResponse也提供了一系列的方法,主要相關方法如下:
? 需要注意的是,addHeader、addIntHeader、addDateHeader都有一個對應的setxxxx方法,兩者的區別就如同集合和列表,setxxxx方法不允許出現重復的header,而addxxxx方法可以;setContentType、setCharacterEncoding方法皆是是指返回給客戶端的內容的編碼方式的,推薦直接使用setContentType設置客戶端內容的MIME類型及編碼方式,比如setContentType("text/html; charset=UTF-8")等價于setContentType("text/html");setCharacterEncoding("charset=UTF-8")兩條語句同時執行。
? 這里,為了演示上面這些方法,我們將ResponseTestServlet中的doGet方法修改如下:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ? ? ? ? ? ??? ??? ??? ??? ?ServletException, IOException {
? //設置返回客戶端的contentType
? response.setContentType("text/html;charset=utf-8");
? //設置狀態碼
? //response.setStatus(500);
? //response.sendError(500);
? PrintWriter out = response.getWriter();
? //out.println("雖然我的狀態為500,但是信息正常輸出了");
? //添加類型為String的header
? response.addHeader("Location", "#");
? //添加類型為long的header
? response.addDateHeader("Date", new Date().getTime());
? //創建一個Cookie
? Cookie cookie = new Cookie("name", "李子樹");
? //添加一個cookie
? response.addCookie(cookie);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
? 其執行結果如下圖所示,我們添加的header都能看到:
3.發送響應消息體
? 前面說了這么多,到了這里才是真正的重頭戲!這里才是最直觀的響應給客戶看到的內容,在早期JSP還沒有誕生的時代,許多動態頁面是通過在Serlvet中使用HttpServletResponse輸出到頁面上的,就算到現在,教材上仍有這部分的演示代碼。下面,就讓我們一起來看下HttpServletResponse是如何發送消息體到客戶端的。
? 首先我們來看兩個HttpServletResponse提供的兩個方法:
? 從中我們可以看到,getOutputStream()方法返回ServletOutputStream對象,更適合向客戶端寫入二進制數據,并且Servlet容器不會對這些二進制數據進行編碼,因此我們常用ServletOutputStream來向客戶端發送如圖片、文件等內容;對于getWriter()方法返回的PrintWriter對象,里面封裝了更多的寫入字符文本的函數,并且我們上文提到的setContentType()方法設置的MIME類型對其輸出內容有效,因此也可以很好地解決中文亂碼問題。
? 還有一點需要注意的是,這兩個方法在一個response對象中不可以同時調用,否則會拋出一個IllegalStateException,也就是非法狀態異常,因為輸出流只能有一個(如果可以多次獲取的話,客戶端又如何確認哪個Http響應是最后一個呢)。
? 下面我們對來簡單的介紹下ServletOutputStream對象和PrintWriter對象中的方法,我們首先來看下ServletOutputStream這個對象(抽象類)的概述(Outline),可以看到,其重載了幾乎可以輸出各種數據類型的print()、println()方法,但是通過查看源碼可以發現,這些方法都是通過其父類OutputStream(java.io.OutputStream)的write()方法進行的消息體的輸出。
? 下面我們來看下PrintWriter對象的概述,其方法較多,我們只截取部分主要方法,如下圖所示,PrintWriter中提供的輸出方法更多,其輸出方法都是通過Writer(java.io.Writer)類中的write()方法來進行的消息體的輸出。
?
? 因為PrintWriter的輸出功能在前面已經使用N遍了,下面我們主要演示下如何通過ServletOutputStream來輸出內容下面我們簡單的通過代碼演示下ServletOutputStream的使用,我們在ResponseTestServlet中的doGet中代碼修改如下(注釋之前的部分):
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ? ? ? ? ? ??? ??? ??? ??? ?ServletException, IOException {
? //設置返回客戶端的contentType
?? ?response.setContentType("text/html;charset=utf-8");
??
? //...
??
? ServletOutputStream out = response.getOutputStream();
? //通過ServletOutputStream向客戶端輸出值
? out.print("We Are Young Man!");
}
1
2
3
4
5
6
7
8
9
10
? 其執行結果如下圖所示,瀏覽器中輸出了Servlet給的響應。
? 這么看,ServletOutputStream和PrintWriter似乎沒什么區別,ServletOutputStream一樣可以輸出字符串呀,但是,注意了,當我們吧輸出內容改為中文,代碼修改為out.print("我們不一樣!");,讓我在來看下執行結果:
? 啥情況,居然發生了錯誤?代碼中不是通過setContentType設置了編碼格式為UTF-8了么,為什么頁面中會提示不是ISO 8859-1字符?這里我們在回過頭來看一句話,ServletOutputStream輸出二進制數據,并且Servlet容器不會對這些二進制數據進行編碼,這里就是說,你輸入二進制流是什么,Servlet容器不會對你的輸出流編碼,因此上面setContentType是無效的。那有為什么會產生異常呢,我們來看下ServletOutputStream中的print(String s)的源碼。(注意,println方法中調用的print方法)
public void print(String s) throws IOException {
? if (s==null) s="null";
? int len = s.length();
? for (int i = 0; i < len; i++) {
? ? char c = s.charAt (i);
? ? // XXX NOTE: ?This is clearly incorrect for many strings,
? ? // but is the only consistent approach within the current
? ? // servlet framework. ?It must suffice until servlet output
? ? // streams properly encode their output.
? ? //
? ? if ((c & 0xff00) != 0) { ? ? ? ?// high order byte must be zero
? ? ? String errMsg = lStrings.getString("err.not_iso8859_1");
? ? ? Object[] errArgs = new Object[1];
? ? ? errArgs[0] = Character.valueOf(c);
? ? ? errMsg = MessageFormat.format(errMsg, errArgs);
? ? ? throw new CharConversionException(errMsg);
? ? }
? ? write (c);
? }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
? 注意下中間的一段注釋,明確的告知了這個方法對許多字符是不正確的,iso 8859-1編碼方式完全不支持中文,因此這里在轉換的過程中會直接的拋出異常,我們在上個運行結果上看到的報錯信息的根由也是在此。
? 通過源碼我們也可以看到,print并沒有進行轉碼,只是判斷一個字節的高地址的一個字節(8位)是否為0(注:iso 8859-1只使用了一個字節來進行編碼),一次來判斷字符是否是iso 8859-1字符集中的字符。那這樣的話,ServletOutputStream就真的無法輸出中文了么?
? 山重水復疑無路,柳暗花明又一村。如果Servlet容器不對二進制數據進行任何的處理,那么,我們是不是可以換個思路?直接將String轉為指定編碼方式的byte[](字節數組),并通過ServletOutputStream中的write(byte b[])方法將字符數組輸出的到客戶端。對應的,我們將上面的代碼修改如下:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ? ? ? ? ? ??? ??? ??? ??? ?ServletException, IOException {
? //設置返回客戶端的contentType
?? ?response.setContentType("text/html;charset=utf-8");
??
? //...
??
? ServletOutputStream out = response.getOutputStream();
? //通過ServletOutputStream向客戶端輸出值
? //通過getBytes獲取字節數組,并指定編碼方式
? out.print("我們不一樣!".getBytes("UTF-8"));
}
1
2
3
4
5
6
7
8
9
10
11
? 其運行結果也如下所示:
? 我么可以看到,瀏覽器中正常的顯示了中文輸出。但是如果我們的每個含有中文的字符串都需要使用這種方式輸出,那不是態麻煩了。這也是我們在描述ServletOutputStream是說的,其適合(suitable)輸出二進制數據。因此在對客戶端的Http請求進行響應式,我們也要選擇合理的輸出方式。
4.總結
? 本文主要講解了Servlet如何對Http請求進行響應,Http響應對應Http請求的三部分內容,分別為響應行、響應頭和消息體,以及對應的如何通過HttpServletResponse設置對應的狀態碼、響應頭,并詳細的解釋了getOutputStream()和getWriter()的區別及其使用場景。
參考閱讀:Http1.1狀態碼定義
? 又到了分隔線以下,本文到此就結束了,本文內容全部都是由博主自己進行整理并結合自身的理解進行總結,如果有什么錯誤,還請批評指正。
? Java web這一專欄會是一個系列博客,喜歡的話可以持續關注,如果本文對你有所幫助,還請還請點贊、評論加關注。
? 有任何疑問,可以評論區留言。
————————————————
版權聲明:本文為CSDN博主「李子樹_」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_34666857/article/details/104838171
總結
以上是生活随笔為你收集整理的Servlet基础之HttpServletResponse详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SpringBoot: SpringBo
- 下一篇: 易贷在线不退款怎么办