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