板塊一 Servlet編程:第四節 HttpServletResponse對象全解與重定向
- 一、什么是HttpServletResponse
- 二、響應數據的常用方法
- 三、響應亂碼問題
- 字符流亂碼
- 字節流亂碼
- 四、重定向:sendRedirect
- 請求轉發和重定向的區別
在上一節中,我們系統的學習了請求響應在Servlet中
service()
方法的第一個形參HttpServletRequest(請求)對象,這一節中我們將學習它的兄弟,service()
方法的第二個形參HttpServletResponse(響應)對象
一、什么是HttpServletResponse
在我們已然熟悉的瀏覽器訪問Servlet的過程中。Request和Response 對象分別代表請求和響應:通過Request對象獲取客戶端數據;通過 Response 對象向客戶端輸出數據:
service()
方法中形參接收的是HttpServletResponse接口的實例化對象,它繼承自ServletResponse接口,專門用來封裝HTTP響應消息,由于HTTP響應消息分為狀態行、響應消息頭、消息體三部分(詳見HTTP協議理論與服務器請求響應原理小節),因此在HttpServletResponse中定義了狀態行、響應消息頭、消息體三部分。
- 狀態行部分
響應消息頭包含了關于響應的附加信息,例如內容類型、內容長度、緩存控制等。由setStatus(int status)
方法實現,該方法用于設置HTTP響應消息的狀態碼,并生成相應代碼;默認會生成一個狀態碼為200的狀態行; - 響應消息頭部分
響應消息頭包含了關于響應的附加信息,例如內容類型、內容長度、緩存控制等。可以使用setHeader(String name, String value)
方法設置響應消息頭的字段和值,例如setHeader("Content-Type", "text/html")
設置內容類型為HTML。如果要設置相同字段的多個值,可以使用addHeader(String name, String value)
方法,例如addHeader("Set-Cookie", "cookie1=value1")
。此外還可以使用一些特定的方法來設置常見的響應消息頭,例如setContentType(String type)
、setContentLength(int len)
等 - 消息體部分
消息體包含了實際的響應數據。可以通過獲取ServletOutputStream或PrintWriter對象來寫入響應消息體。getOutputStream()
方法返回一個可以寫入二進制數據的ServletOutputStream對象。
getWriter()
方法返回一個可以寫入字符數據的PrintWriter對象。
可以使用這些對象的方法將數據寫入響應消息體,例如print(String s)、write(byte[] b)
等。
二、響應數據的常用方法
接收到客戶端請求后,可以通過HttpServletResponse對象直接進行響應,響應時需要獲取輸出流。
有兩種形式:
getWriter()
獲取字符流(只能響應字符串)getOutputStream()
獲取字節流(能響應一切數據)
響應回的數據到客戶端被瀏覽器解析
注意:兩者不能同時使用
實例
在start.java導入PrintWriter類,并在service()中寫入測試代碼
// 獲取字符輸出流
PrintWriter writer = resp.getWriter();
//輸出數據
writer.write("Hello");
啟動服務器,在瀏覽器中訪問得
在start.java中導入ServletOutputStream類,并在service()中寫入測試代碼
//得到字節輸出流
ServletOutputStream out = resp.getOutputStream();
// 輸出數據
out.write("Hi".getBytes());
啟動服務器,在瀏覽器中訪問得
但當兩者同時使用時
start.java
package www.caijiyuan;import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;@WebServlet("/start")
public class start extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 獲取字符輸出流PrintWriter writer = resp.getWriter();//輸出數據writer.write("Hello");//得到字節輸出流ServletOutputStream out = resp.getOutputStream();// 輸出數據out.write("Hi".getBytes());}
}
啟動服務器,在瀏覽器中訪問,只得到了第一個的打印內容
這是為什么呢?查看報錯信息
原來是
getWriter()
已經調用過response對象了,如果再響應一次response對象就已經不存在了
三、響應亂碼問題
在上一節中我們使用request.setCharacterEncoding("UTF-8");
解決了請求時中文亂碼的問題,同樣,在響應時也存在中文亂碼問題。這是因為服務器響應的數據也會經過網絡傳輸,服務器端有一種編碼方式,在客戶端也存在一種編碼方式,當兩端使用的編碼方式不同時則出現亂碼。
字符流亂碼
對于getWriter()
獲取到的字符流,響應中文必定出亂碼,由于服務器端在進行編碼時默認會使用ISO-8859-1格式的編碼,該編碼方式并不支持中文。要解決該種亂碼只能在服務器端告知服務器使用一種能夠支持中文的編碼格式,這也是我們在解決請求時中文亂碼的方法
response.setCharacterEncoding("UTF-8");
此時還只完成了一半的工作
要保證數據正確顯示,還需要指定客戶端的解碼方式
response.setHeader("content-type", "text/html; charset=UTF-8");
兩端指定編碼后,亂碼就解決了。一句話:保證發送端和接收端的編碼一致
實例
我們在start.java的service()中寫入測試測試代碼,試圖打印中文
// 獲取字符輸出流
PrintWriter writer = resp.getWriter();
//輸出數據
writer.write("湯米尼克");
啟動服務器,在瀏覽器中訪問,發現輸出中文亂碼
設置服務器和客戶端的編碼格式統一
// 設置服務端的編碼
resp.setCharacterEncoding("UTF-8");
// 設置客戶端的響應類型及編碼
resp. setHeader("content-type", "text/html; charset=UTF-8");
// 獲取字符輸出流
PrintWriter writer = resp.getWriter();
// 輸出數據
writer.write("湯米尼克");
重啟瀏覽器,再在瀏覽器中訪問就解決問題了
理解了原理,其實我們還可以同時設置客戶端和服務端的編碼方式
response.setContentType( "text/html; charset=UTF-8");
這一句就可以替換上面的兩句
字節流亂碼
對于getOutputStream()
方式獲取到的字節流,響應中文時,由于本身就是傳輸的字節,所以此時可能出現亂碼,也可能正確顯示。當服務器端給的字節恰好和客戶端使用的編碼方式一致時則文本正確顯示,否則出現亂碼。無論如何我們都應該準確掌握服務器和客戶端使用的是那種編碼格式,以確保數據正確顯示。
因此,字節流亂碼的解決方式與上面字符流亂碼的解決方式一樣,在響應發出之前同時設置服務器和客戶端的編碼格式統一即可
response.setContentType( "text/html; charset=UTF-8");
四、重定向:sendRedirect
重定向是一種服務器為指導的客戶端行為。
怎么理解這句話呢?客戶端發出一個請求,被服務器接收處理后進行響應,在響應的同時,服務器會給客戶端一個新的地址(下次請求的地址),當客戶端接收到響應后,會立刻、馬上自動根據服務器給的新地址發起第二個請求,服務器接收請求并作出響應,重定向完成。可以看出這個過程中有兩個請求存在,其中兩個Servlet的Request對象并不共享、不能傳值,屬于客戶端行為。
在Servlet中重定向的語句為
response.sendRedirect("url");
實例:從start.java重定向到after.java的過程
在start.java的service()
中寫入重定向前的測試代碼
System.out.println("這里是start");
resp.sendRedirect("after");
在after.java的service()
中寫入重定向到底測試代碼
System.out.println("這里是after");
啟動服務器,在瀏覽器中輸入start的地址
回車訪問后地址立即跳轉到after,說明重定向的地址欄會發生改變
同時控制臺輸出了
那么重定向在服務器中的響應頭是如何實現的?
如下圖,在開發者工具中打開響應頭的內容
會發現start文件響應行的狀態碼是302,這就是重定向的狀態碼
并且響應頭鍵值對中Location鍵的值就是要重定向到的地址:after文件
這與我們在第一節 HTTP協議理論與服務器請求響應原理中學習的響應頭的知識首尾呼應起來了
請求轉發和重定向的區別
上一節中我們學習了Request對象的請求轉發,這一節又學習了Response對象的重定向,兩兄弟讓人傻傻分不清,必須好好區分區分
請求轉發 | 重定向 |
---|---|
request.getRequestDispatcher("url").forward(request, response); | response.sendRedirect("url"); |
服務器端行為 | 客戶端行為 |
一次請求,Request域中數據共享 | 兩次請求,Request域中數據不共享 |
地址欄不發生變化 | 地址欄發生變化 |
跳轉只能在當前站點內 | 跳轉任意地址 |
在這一節中我們學習了HttpServletResponse對象,學習了字符流字節流響應方法、重定向方法。不禁思考,Servlet作為“后端”,在Web交互中最重要的作用就是傳遞各種數據,但目前我們學到的傳值的方法還知之甚少,在下一節中我們將學習Cookie對象、HttpSession對象、ServletContext對象,它們作為不同特點的容器在Servlet上可以實現不同范圍的傳值