柚子快報激活碼778899分享:Servlet中的請求與響應(yīng)
柚子快報激活碼778899分享:Servlet中的請求與響應(yīng)
Request和Response
1.Request和Response的概述2.Request對象2.1 Request繼承體系2.2 Request獲取請求數(shù)據(jù)2.3 解決post請求亂碼問題 *2.4 Request請求轉(zhuǎn)發(fā)(-,*)2.5 request的生命周期
3.HTTP響應(yīng)詳解(理解)1.使用抓包查看響應(yīng)報文協(xié)議內(nèi)容2.HTTP響應(yīng)報文協(xié)議介紹
4.Response對象1 Response對象介紹2 Response設(shè)置響應(yīng)數(shù)據(jù)功能介紹3 Respones請求重定向4 路徑問題5 Response響應(yīng)字符數(shù)據(jù)6 Response響應(yīng)字節(jié)數(shù)據(jù)
回顧總結(jié)
1.Request和Response的概述
重點
service方法的兩個參數(shù)request和response是由tomcat創(chuàng)建的 void service(ServletRequest var1, ServletResponse var2)request 表示請求數(shù)據(jù), tomcat將瀏覽器發(fā)送過來的請求數(shù)據(jù)解析并封裝到request對象中 servlet開發(fā)者可以通過request對象獲得請求數(shù)據(jù)response 表示響應(yīng)數(shù)據(jù),服務(wù)器發(fā)送給瀏覽器的數(shù)據(jù) servlet開發(fā)者可以通過response對象設(shè)置響應(yīng)數(shù)據(jù)
==Request是請求對象,Response是響應(yīng)對象。==這兩個對象在我們使用Servlet的時候有看到:
此時,我們就需要思考一個問題request和response這兩個參數(shù)的作用是什么?
request:獲取請求數(shù)據(jù)
瀏覽器會發(fā)送HTTP請求到后臺服務(wù)器[Tomcat]HTTP的請求中會包含很多請求數(shù)據(jù)[請求行+請求頭+請求體]后臺服務(wù)器[Tomcat]會對HTTP請求中的數(shù)據(jù)進行解析并把解析結(jié)果存入到一個對象中所存入的對象即為request對象,所以我們可以從request對象中獲取請求的相關(guān)參數(shù)獲取到數(shù)據(jù)后就可以繼續(xù)后續(xù)的業(yè)務(wù),比如獲取用戶名和密碼就可以實現(xiàn)登錄操作的相關(guān)業(yè)務(wù) response:設(shè)置響應(yīng)數(shù)據(jù)
業(yè)務(wù)處理完后,后臺就需要給前端返回業(yè)務(wù)處理的結(jié)果即響應(yīng)數(shù)據(jù)把響應(yīng)數(shù)據(jù)封裝到response對象中后臺服務(wù)器[Tomcat]會解析response對象,按照[響應(yīng)行+響應(yīng)頭+響應(yīng)體]格式拼接結(jié)果瀏覽器最終解析結(jié)果,把內(nèi)容展示在瀏覽器給用戶瀏覽
對于上述所講的內(nèi)容,我們通過一個案例來初步體驗下request和response對象的使用。
@WebServlet("/demo3")
public class ServletDemo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//使用request對象 獲取請求數(shù)據(jù)
String name = request.getParameter("name");//url?name=zhangsan
//使用response對象 設(shè)置響應(yīng)數(shù)據(jù)
response.setHeader("content-type","text/html;charset=utf-8");
response.getWriter().write("
"+name+",歡迎您!
");}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("Post...");
}
}
啟動成功后就可以通過瀏覽器來訪問,并且根據(jù)傳入?yún)?shù)的不同就可以在頁面上展示不同的內(nèi)容:
小結(jié)
我們主要認(rèn)識了下request對象和reponse對象:
request對象是用來封裝瀏覽器請求tomcat服務(wù)器數(shù)據(jù)的對象response對象是用來封裝tomcat服務(wù)器響應(yīng)給瀏覽器的數(shù)據(jù)的對象
目前我們只知道這兩個對象是用來干什么的,那么它們具體是如何實現(xiàn)的,就需要我們繼續(xù)深入的學(xué)習(xí)。接下來,就先從Request對象來學(xué)習(xí),主要學(xué)習(xí)下面這些內(nèi)容:
request繼承體系 request獲取請求參數(shù) request請求轉(zhuǎn)發(fā)(了解)
2.Request對象
2.1 Request繼承體系
在學(xué)習(xí)這節(jié)內(nèi)容之前,我們先思考一個問題,前面在介紹Request和Reponse對象的時候,比較細(xì)心的同學(xué)可能已經(jīng)發(fā)現(xiàn):
當(dāng)我們的Servlet類實現(xiàn)的是Servlet接口的時候,service方法中的參數(shù)是ServletRequest和ServletResponse當(dāng)我們的Servlet類繼承的是HttpServlet類的時候,doGet和doPost方法中的參數(shù)就變成HttpServletRequest和HttpServletReponse
那么,
ServletRequest和HttpServletRequest的關(guān)系是什么?request對象是有誰來創(chuàng)建的?request提供了哪些API,這些API從哪里查?
首先,我們先來看下Request的繼承體系:
ServletRequest request = new RequestFacade();
從上圖中可以看出,ServletRequest和HttpServletRequest都是Java提供的,所以我們可以打開JavaEE提供的API文檔,打開后可以看到:
所以ServletRequest和HttpServletRequest是繼承關(guān)系,并且兩個都是接口,接口是無法創(chuàng)建對象,這個時候就引發(fā)了下面這個問題:
這個時候,我們就需要用到Request繼承體系中的RequestFacade:
該類實現(xiàn)了HttpServletRequest接口,也間接實現(xiàn)了ServletRequest接口。Servlet類中的service方法、doGet方法或者是doPost方法最終都是由Web服務(wù)器[Tomcat]來調(diào)用的,所以Tomcat提供了方法參數(shù)接口的具體實現(xiàn)類,并完成了對象的創(chuàng)建要想了解RequestFacade中都提供了哪些方法,我們可以直接查看JavaEE的API文檔中關(guān)于ServletRequest和HttpServletRequest的接口文檔,因為RequestFacade實現(xiàn)了其接口就需要重寫接口中的方法
對于上述結(jié)論,要想驗證,可以編寫一個Servlet,在方法中把request對象打印下,就能看到最終的對象是不是RequestFacade,代碼如下:
@WebServlet("/demo2")
public class ServletDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println(request);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
啟動服務(wù)器,運行訪問http://localhost:8080/request-demo/demo2,得到運行結(jié)果:
小結(jié)
Request的繼承體系為ServletRequest(最大父接口)–>HttpServletRequest(可以處理http協(xié)議的請求接口)–>RequestFacade(tomcat定義的實現(xiàn)類)Tomcat需要解析請求數(shù)據(jù),封裝為request對象,并且創(chuàng)建request對象傳遞到service方法使用request對象,可以查閱JavaEE API文檔的HttpServletRequest接口中方法說明
2.2 Request獲取請求數(shù)據(jù)
HTTP請求數(shù)據(jù)總共分為三部分內(nèi)容,分別是請求行、請求頭、請求體,對于這三部分內(nèi)容的數(shù)據(jù),分別該如何獲取,首先我們先來學(xué)習(xí)請求行數(shù)據(jù)如何獲取?
2.2.1 獲取請求行數(shù)據(jù)
請求行包含三塊內(nèi)容,分別是請求方式、請求資源路徑、HTTP協(xié)議及版本
對于這三部分內(nèi)容,request對象都提供了對應(yīng)的API方法來獲取,具體如下:
獲取請求方式: GET
String getMethod()
獲取虛擬目錄(項目訪問路徑): /request-demo
String getContextPath()
獲取URL(統(tǒng)一資源定位符): http://localhost:8080/request-demo/req1
StringBuffer getRequestURL()
獲取URI(統(tǒng)一資源標(biāo)識符): /request-demo/req1
String getRequestURI()
獲取請求參數(shù)(GET方式): username=zhangsan&password=123
String getQueryString()
介紹完上述方法后,咱們通過代碼把上述方法都使用下:
/**
* request 獲取請求數(shù)據(jù)
*/
@WebServlet("/req1")
public class RequestDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// String getMethod():獲取請求方式: GET
String method = req.getMethod();
System.out.println(method);//GET
// String getContextPath():獲取虛擬目錄(項目訪問路徑):/request-demo
String contextPath = req.getContextPath();
System.out.println(contextPath);
// StringBuffer getRequestURL(): 獲取URL(統(tǒng)一資源定位符):http://localhost:8080/request-demo/req1
StringBuffer url = req.getRequestURL();
System.out.println(url.toString());
// String getRequestURI():獲取URI(統(tǒng)一資源標(biāo)識符): /request-demo/req1
String uri = req.getRequestURI();
System.out.println(uri);
// String getQueryString():獲取請求參數(shù)(GET方式): username=zhangsan
String queryString = req.getQueryString();
System.out.println(queryString);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
啟動服務(wù)器,訪問http://localhost:8080/request-demo/req1?username=zhangsan&passwrod=123,獲取的結(jié)果如下:
2.2.2 獲取請求頭數(shù)據(jù)
對于請求頭的數(shù)據(jù),格式為key: value如下:
所以根據(jù)請求頭名稱獲取對應(yīng)值的方法為:
String getHeader(String name) //參數(shù)name書寫的是請求頭冒號左邊的內(nèi)容例如:User-Agent
//User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36
接下來,在代碼中如果想要獲取客戶端瀏覽器的版本信息,則可以使用
/**
* request 獲取請求數(shù)據(jù)
*/
@WebServlet("/req1")
public class RequestDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//獲取請求頭: user-agent: 瀏覽器的版本信息
String agent = req.getHeader("user-agent");
System.out.println(agent);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
重新啟動服務(wù)器后,http://localhost:8080/request-demo/req1?username=zhangsan&passwrod=123,獲取的結(jié)果如下:
2.2.3 獲取請求體數(shù)據(jù)
瀏覽器在發(fā)送GET請求的時候是沒有請求體的,所以需要把請求方式變更為POST,請求體中的數(shù)據(jù)格式如下:
對于請求體中的數(shù)據(jù),Request對象提供了如下兩種方式來獲取其中的數(shù)據(jù),分別是:
獲取字節(jié)輸入流,如果前端發(fā)送的是字節(jié)數(shù)據(jù),比如傳遞的是文件數(shù)據(jù),則使用該方法
ServletInputStream getInputStream()
該方法可以獲取字節(jié)和字符數(shù)據(jù)
獲取字符輸入流,如果前端發(fā)送的是純文本數(shù)據(jù),則使用該方法
BufferedReader getReader()
接下來,大家需要思考,要想獲取到請求體的內(nèi)容該如何實現(xiàn)?
具體實現(xiàn)的步驟如下:
1.準(zhǔn)備一個頁面,在頁面中添加form表單,用來發(fā)送post請求
2.在Servlet的doPost方法中獲取請求體數(shù)據(jù)
3.在doPost方法中使用request的getReader()或者getInputStream()來獲取
4.訪問測試
在項目的webapp目錄下添加一個html頁面,名稱為:req.html
在Servlet的doPost方法中獲取數(shù)據(jù)
/**
* request 獲取請求數(shù)據(jù)
*/
@WebServlet("/req1")
public class RequestDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//在此處獲取請求體中的數(shù)據(jù)
}
}
調(diào)用getReader()或者getInputStream()方法,因為目前前端傳遞的是純文本數(shù)據(jù),所以我們采用getReader()方法來獲取
/**
* request 獲取請求數(shù)據(jù)
*/
@WebServlet("/req1")
public class RequestDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//獲取post 請求體:請求參數(shù)
//1. 獲取字符輸入流
BufferedReader br = req.getReader();
//2. 讀取數(shù)據(jù)
String line = br.readLine();
System.out.println(line);
}
}
注意
BufferedReader流是通過request對象來獲取的,當(dāng)請求完成后request對象就會被銷毀,request對象被銷毀后,BufferedReader流就會自動關(guān)閉,所以此處就不需要手動關(guān)閉流了。
啟動服務(wù)器,通過瀏覽器訪問http://localhost:8080/request-demo/req.html
點擊提交按鈕后,就可以在控制臺看到前端所發(fā)送的請求數(shù)據(jù)
小結(jié)
HTTP請求數(shù)據(jù)中包含了請求行、請求頭和請求體,針對這三部分內(nèi)容,Request對象都提供了對應(yīng)的API方法來獲取對應(yīng)的值:
請求行
getMethod()獲取請求方式 GET POSTgetContextPath()獲取項目訪問虛擬路徑 /day06getRequestURL()獲取請求URL http://localhost:8080/day06/demo01getRequestURI()獲取請求URI /day06/demo01getQueryString()獲取GET請求方式的請求參數(shù),獲取的請求參數(shù)放到一個字符串中 了解 請求頭
getHeader(String name)根據(jù)請求頭名稱獲取其對應(yīng)的值 了解 請求體
注意: 瀏覽器發(fā)送的POST請求才有請求體數(shù)據(jù)如果是純文本數(shù)據(jù):getReader() 了解如果是字節(jié)數(shù)據(jù)如文件數(shù)據(jù):getInputStream()
2.2.4 獲取請求參數(shù)的通用方式(很重要)
在學(xué)習(xí)下面內(nèi)容之前,我們先提出兩個問題:
什么是請求參數(shù)?請求參數(shù)和請求數(shù)據(jù)的關(guān)系是什么?
1.什么是請求參數(shù)?
為了能更好的回答上述兩個問題,我們拿用戶登錄的例子來說明
1.1 想要登錄網(wǎng)址,需要進入登錄頁面
1.2 在登錄頁面輸入用戶名和密碼
1.3 將用戶名和密碼提交到后臺
1.4 后臺校驗用戶名和密碼是否正確
1.5 如果正確,則正常登錄,如果不正確,則提示用戶名或密碼錯誤
上述例子中,用戶名和密碼其實就是我們所說的請求參數(shù)。
get請求:請求參數(shù)位于url后面。
post請求:請求參數(shù)位于請求體中。
2.什么是請求數(shù)據(jù)?
請求數(shù)據(jù)則是包含請求行、請求頭和請求體的所有數(shù)據(jù)
3.請求參數(shù)和請求數(shù)據(jù)的關(guān)系是什么?
3.1 請求參數(shù)是請求數(shù)據(jù)中的部分內(nèi)容
3.2 如果是GET請求,請求參數(shù)在請求行中
3.3 如果是POST請求,請求參數(shù)一般在請求體中
對于請求參數(shù)的獲取,常用的有以下兩種:
GET方式:
String getQueryString()
POST方式:
BufferedReader getReader();
有了上述的知識儲備,我們來實現(xiàn)一個案例需求:
(1)發(fā)送一個GET請求并攜帶用戶名,后臺接收后打印到控制臺
(2)發(fā)送一個POST請求并攜帶用戶名,后臺接收后打印到控制臺
此處大家需要注意的是GET請求和POST請求接收參數(shù)的方式不一樣,具體實現(xiàn)的代碼如下:
@WebServlet("/req1")
public class RequestDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String result = req.getQueryString();
System.out.println(result);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
BufferedReader br = req.getReader();
String result = br.readLine();
System.out.println(result);
}
}
GET請求和POST請求獲取請求參數(shù)的方式不一樣,在獲取請求參數(shù)這塊該如何實現(xiàn)呢?
要想實現(xiàn),我們就需要思考:
GET請求方式和POST請求方式區(qū)別主要在于獲取請求參數(shù)的方式不一樣,是否可以提供一種統(tǒng)一獲取請求參數(shù)的方式,從而統(tǒng)一doGet和doPost方法內(nèi)的代碼?
解決方案一:
@WebServlet("/req1")
public class RequestDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//獲取請求方式
String method = req.getMethod();
//獲取請求參數(shù)
String params = "";
if("GET".equals(method)){
params = req.getQueryString();
}else if("POST".equals(method)){
BufferedReader reader = req.getReader();
params = reader.readLine();
}
//將請求參數(shù)進行打印控制臺
System.out.println(params);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
使用request的getMethod()來獲取請求方式,根據(jù)請求方式的不同分別獲取請求參數(shù)值,這樣就可以解決上述問題,但是以后每個Servlet都需要這樣寫代碼,實現(xiàn)起來比較麻煩,這種方案我們不采用
解決方案二:
request對象已經(jīng)將上述獲取請求參數(shù)的方法進行了封裝,并且request提供的方法實現(xiàn)的功能更強大,以后只需要調(diào)用request提供的方法即可,在request的方法中都實現(xiàn)了哪些操作?
(1)根據(jù)不同的請求方式獲取請求參數(shù),獲取的內(nèi)容如下:
(2)把獲取到的內(nèi)容進行分割,內(nèi)容如下:
(3)把分割后端數(shù)據(jù),存入到一個Map集合中:
注意:因為參數(shù)的值可能是一個,也可能有多個,所以Map的值的類型為String數(shù)組。
基于上述理論,request對象為我們提供了如下方法:
獲取所有參數(shù)Map集合
Map
根據(jù)名稱獲取參數(shù)值(數(shù)組)
String[] getParameterValues(String name)參數(shù)name是前端提交請求參數(shù)的等號左邊的key(name)==>username=zhangsan===>想獲取zhangsan===>getParameterValues("username");
根據(jù)名稱獲取參數(shù)值(單個值)
String getParameter(String name)注意:如果參數(shù)的name有多個值誰在前面先獲取誰
接下來,我們通過案例來把上述的三個方法進行實例演示:
1.修改req.html頁面,添加愛好選項,愛好可以同時選多個
2.在Servlet代碼中獲取頁面?zhèn)鬟fGET請求的參數(shù)值
2.1獲取GET方式的所有請求參數(shù)
/**
* request 通用方式獲取請求參數(shù)
*/
@WebServlet("/req2")
public class RequestDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//GET請求邏輯
System.out.println("get....");
//1. 獲取所有參數(shù)的Map集合
Map
for (String key : map.keySet()) {
// username:zhangsan lisi
System.out.print(key+":");
//獲取值
String[] values = map.get(key);
for (String value : values) {
System.out.print(value + " ");
}
System.out.println();
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
獲取的結(jié)果為:
2.2獲取GET請求參數(shù)中的愛好,結(jié)果是數(shù)組值
/**
* request 通用方式獲取請求參數(shù)
*/
@WebServlet("/req2")
public class RequestDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//GET請求邏輯
//...
System.out.println("------------");
String[] hobbies = req.getParameterValues("hobby");
for (String hobby : hobbies) {
System.out.println(hobby);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
獲取的結(jié)果為:
2.3獲取GET請求參數(shù)中的用戶名和密碼,結(jié)果是單個值
/**
* request 通用方式獲取請求參數(shù)
*/
@WebServlet("/req2")
public class RequestDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//GET請求邏輯
//...
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println(username);
System.out.println(password);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
獲取的結(jié)果為:
3.在Servlet代碼中獲取頁面?zhèn)鬟fPOST請求的參數(shù)值
3.1將req.html頁面form表單的提交方式改成post
3.2將doGet方法中的內(nèi)容復(fù)制到doPost方法中即可
小結(jié)
req.getParameter()和getParameterMap()方法使用的頻率會比較高
2.3 解決post請求亂碼問題 *
學(xué)習(xí)目標(biāo)
能夠掌握產(chǎn)生亂碼的原因以及如何解決post亂碼
內(nèi)容講解
html頁面:
【1】從tomcat8開始以后,對于get請求亂碼,tomcat已經(jīng)解決。對于post請求中文亂碼沒有解決,需要我們自己處理。
【2】post請求亂碼產(chǎn)生的原因和解決思路
說明:
1)頁面使用的編碼表是UTF-8編碼,tomcat使用的是默認(rèn)編碼表ISO-8859-1進行解碼,編碼和解碼使用的編碼表不一致導(dǎo)致亂碼。
2)解決思路:先按照ISO-8859-1編碼,在按照UTF-8進行重新解碼
【3】解決方案
解決方案有三種:
1.方案一
【1】方式一
使用URLEncoder類進行編碼:static String encode(String s, String enc)
參數(shù):
s:編碼的字符串
enc:使用編碼表
使用URLDecoder進行解碼:static String decode(String s, String enc)
參數(shù):
s:解碼的字符串
enc:使用編碼表
2.方案二
【2】方式二:
使用String類中的方法進行編碼: byte[] getBytes(String charsetName)
參數(shù)表示指定的編碼表,返回值表示編碼后的字節(jié)數(shù)組
使用String類中的構(gòu)造方法進行解碼:String(byte[] bytes, String charsetName)
參數(shù):
bytes:字節(jié)數(shù)組
charsetName:表示指定的編碼表
返回值:解碼后的字符串
3.方案三
【3】方式三:
如果是get請求,tomcat8底層已經(jīng)幫助我們解決完了,我們只需要解決post亂碼即可,但是上述
兩種方式對于post請求可以解決亂碼,對于get請求本身獲取到的已經(jīng)是正確的數(shù)據(jù),處理
后又亂碼了。
我們的想法是:get請求不用我們自己書寫代碼處理亂碼,只需要我們書寫代碼處理post亂碼。
我們接下來學(xué)習(xí)第三種解決方案:
只解決來自于請求體數(shù)據(jù)的亂碼。而get請求體沒有數(shù)據(jù),post請求體含有數(shù)據(jù),所以我們可以理解為第三種處理方案只 是用來解決post亂碼的。使用的api是ServletRequest接口中的:
void setCharacterEncoding(String env)
參數(shù):指定的編碼表
注意:該方式的代碼必須書寫在獲取請求數(shù)據(jù)之前
【4】代碼實現(xiàn)
package com.example.sh.web;
import com.example.sh.pojo.User;
import com.example.sh.service.UserServcie;
import javax.servlet.ServletException;
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.net.URLDecoder;
import java.net.URLEncoder;
@WebServlet("/httpServletRequestDemo04Servlet")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.獲取瀏覽器的請求數(shù)據(jù)
// String username = request.getParameter("username");
/*
解決post亂碼問題有三種方式:
【1】方式一
使用URLEncoder類進行編碼:static String encode(String s, String enc)
參數(shù):
s:編碼的字符串
enc:使用編碼表
使用URLDecoder進行解碼:static String decode(String s, String enc)
參數(shù):
s:解碼的字符串
enc:使用編碼表
*/
//1)編碼 : 使用URLEncoder類進行編碼:static String encode(String s, String enc)
// String encodeUsername = URLEncoder.encode(username, "ISO-8859-1");
// //2)解碼:使用URLDecoder進行解碼:static String decode(String s, String enc)
// username = URLDecoder.decode(encodeUsername, "UTF-8");
/*
解決post亂碼問題有三種方式:
【2】方式二:
使用String類中的方法進行編碼: byte[] getBytes(String charsetName)
參數(shù)表示指定的編碼表,返回值表示編碼后的字節(jié)數(shù)組
使用String類中的構(gòu)造方法進行解碼:String(byte[] bytes, String charsetName)
參數(shù):
bytes:字節(jié)數(shù)組
charsetName:表示指定的編碼表
返回值:解碼后的字符串
*/
//1)編碼 : 使用String類中的方法進行編碼: byte[] getBytes(String charsetName)
// byte[] bytes = username.getBytes("ISO-8859-1");
// //2)解碼:使用String類中的構(gòu)造方法進行解碼:String(byte[] bytes, String charsetName)
// username = new String(bytes, "UTF-8");
//username = new String(username.getBytes("ISO-8859-1"), "UTF-8");
/*
解決post亂碼問題有三種方式:
【3】方式三:
如果是get請求,tomcat8底層已經(jīng)幫助我們解決完了,我們只需要解決post亂碼即可,但是上述
兩種方式對于post請求可以解決亂碼,對于get請求本身獲取到的已經(jīng)是正確的數(shù)據(jù),處理
后又亂碼了。
我們的想法是:get請求不用我們自己書寫代碼處理亂碼,只需要我們書寫代碼處理post亂碼。
我們接下來學(xué)習(xí)第三種解決方案:
只解決來自于請求體數(shù)據(jù)的亂碼。而get請求體沒有數(shù)據(jù),post請求體含有數(shù)據(jù),所以我們可以理解為第三種處理方案只是用來解決
post亂碼的。使用的api是ServletRequest接口中的:
void setCharacterEncoding(String env)
參數(shù):指定的編碼表
注意:該方式的代碼必須書寫在獲取請求數(shù)據(jù)之前
*/
request.setCharacterEncoding("utf-8");//告知tomcat使用UTF-8解碼頁面請求數(shù)據(jù)
// 1.獲取瀏覽器的請求數(shù)據(jù)
String username = request.getParameter("username");
System.out.println("username = " + username);
}
}
內(nèi)容小結(jié)
1.tomcat8以后對于get請求亂碼已經(jīng)處理完畢,我們只需要處理post請求
2.處理post請求亂碼有三種方式:
方式一:
/*
解決post請求亂碼實現(xiàn):
方式一:
編碼:URLEncoder:HTML 格式編碼的實用工具類。編碼方法:
static String encode(String s, String enc) 參數(shù):s 編碼的字符串 enc 編碼使用的編碼表
解碼:URLDecoder : HTML 格式解碼的實用工具類,解碼方法:
static String decode(String s, String enc) 參數(shù):s 解碼的字符串 enc 解碼使用的編碼表
*/
//編碼:URLEncoder:HTML 格式編碼的實用工具類。編碼方法:
// String encode = URLEncoder.encode(username, "ISO-8859-1");
// //解碼:URLDecoder : HTML 格式解碼的實用工具類,解碼方法:
// username = URLDecoder.decode(encode, "UTF-8");
方式二:
/*
解決post請求亂碼實現(xiàn):
方式二:
編碼:使用String類的方法進行編碼:byte[] getBytes(String charsetName) 參數(shù)表示指定的碼表
解碼:使用String類的構(gòu)造方法:String(byte[] bytes, String charsetName) 參數(shù):第一個參數(shù)是字節(jié)數(shù)組 第二個參數(shù)表示指定的碼表
*/
// 編碼:使用String類的方法進行編碼:byte[] getBytes(String charsetName) 參數(shù)表示指定的碼表
// byte[] bytes = username.getBytes("ISO-8859-1");
// // 解碼:使用String類的構(gòu)造方法:String(byte[] bytes, String charsetName) 參數(shù):第一個參數(shù)是字節(jié)數(shù)組 第二個參數(shù)表示指定的碼表
// username = new String(bytes, "UTF-8");
// username = new String(username.getBytes("ISO-8859-1"), "UTF-8");
方式三:解決post請求亂碼使用最多
/*
解決post請求亂碼實現(xiàn):
方式三:推薦使用
使用request對象調(diào)用方法: void setCharacterEncoding(String env) 參數(shù):env指定的編碼表
說明:
1.該方法是用來解決請求體數(shù)據(jù)的亂碼問題。get請求體沒有數(shù)據(jù),post請求體含有數(shù)據(jù)。可以認(rèn)為該方法就是解決post請求亂碼的
2.該方法必須放到獲取所有請求數(shù)據(jù)之前處理亂碼。
*/
//處理請求亂碼
request.setCharacterEncoding("utf-8");
2.4 Request請求轉(zhuǎn)發(fā)(-,*)
請求轉(zhuǎn)發(fā)(forward):一種在服務(wù)器內(nèi)部的資源跳轉(zhuǎn)方式。
(1)瀏覽器發(fā)送請求給服務(wù)器,服務(wù)器中對應(yīng)的資源A接收到請求
(2)資源A處理完請求后將請求發(fā)給資源B
(3)資源B處理完后將結(jié)果響應(yīng)給瀏覽器
(4)請求從資源A到資源B的過程就叫請求轉(zhuǎn)發(fā)
請求轉(zhuǎn)發(fā)的實現(xiàn)方式:
req.getRequestDispatcher("資源B路徑").forward(req,resp);
說明:
1)獲取這轉(zhuǎn)發(fā)器:RequestDispatcher dispatcher = req.getRequestDispatcher("資源B路徑");
2)轉(zhuǎn)發(fā):RequestDispatcher表示轉(zhuǎn)發(fā)器,該接口中有一個方法:forward(request,response)
具體如何來使用,我們先來看下需求:
針對上述需求,具體的實現(xiàn)步驟為:
1.創(chuàng)建一個RequestDemo5類,接收/req5的請求,在doGet方法中打印demo5
2.創(chuàng)建一個RequestDemo6類,接收/req6的請求,在doGet方法中打印demo6
3.在RequestDemo5的方法中使用
? req.getRequestDispatcher(“/req6”).forward(req,resp)進行請求轉(zhuǎn)發(fā)
4.啟動測試
(1)創(chuàng)建RequestDemo5類
/**
* 請求轉(zhuǎn)發(fā)
*/
@WebServlet("/req5")
public class RequestDemo5 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo5...");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
(2)創(chuàng)建RequestDemo6類
/**
* 請求轉(zhuǎn)發(fā)
*/
@WebServlet("/req6")
public class RequestDemo6 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo6...");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
(3)在RequestDemo5的doGet方法中進行請求轉(zhuǎn)發(fā)
/**
* 請求轉(zhuǎn)發(fā)
*/
@WebServlet("/req5")
public class RequestDemo5 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo5...");
//請求轉(zhuǎn)發(fā)
request.getRequestDispatcher("/req6").forward(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
(4)啟動測試
訪問http://localhost:8080/request-demo/req5,就可以在控制臺看到如下內(nèi)容:
說明請求已經(jīng)轉(zhuǎn)發(fā)到了/req6
請求轉(zhuǎn)發(fā)資源間共享數(shù)據(jù):使用Request域?qū)ο?/p>
此處主要解決的問題是把請求從/req5轉(zhuǎn)發(fā)到/req6的時候,如何傳遞數(shù)據(jù)給/req6。
需要使用request對象提供的三個方法:
存儲數(shù)據(jù)到request域[范圍,數(shù)據(jù)是存儲在request對象]中
void setAttribute(String name,Object o);
根據(jù)key獲取值
Object getAttribute(String name);
根據(jù)key刪除該鍵值對
void removeAttribute(String name);
接著上個需求來:
1.在RequestDemo5的doGet方法中轉(zhuǎn)發(fā)請求之前,將數(shù)據(jù)存入request域?qū)ο笾?/p>
2.在RequestDemo6的doGet方法從request域?qū)ο笾蝎@取數(shù)據(jù),并將數(shù)據(jù)打印到控制臺
3.啟動訪問測試
(1)修改RequestDemo5中的方法
@WebServlet("/req5")
public class RequestDemo5 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo5...");
//存儲數(shù)據(jù)
request.setAttribute("msg","hello");
//請求轉(zhuǎn)發(fā)
request.getRequestDispatcher("/req6").forward(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
(2)修改RequestDemo6中的方法
/**
* 請求轉(zhuǎn)發(fā)
*/
@WebServlet("/req6")
public class RequestDemo6 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo6...");
//獲取數(shù)據(jù)
Object msg = request.getAttribute("msg");
System.out.println(msg);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
(3)啟動測試
訪問http://localhost:8080/request-demo/req5,就可以在控制臺看到如下內(nèi)容:
此時就可以實現(xiàn)在轉(zhuǎn)發(fā)多個資源之間共享數(shù)據(jù)。
請求轉(zhuǎn)發(fā)的特點
瀏覽器地址欄路徑不發(fā)生變化 雖然后臺從/req5轉(zhuǎn)發(fā)到/req6,但是瀏覽器的地址一直是/req5,未發(fā)生變化 只能轉(zhuǎn)發(fā)到當(dāng)前服務(wù)器的內(nèi)部資源 不能從一個服務(wù)器通過轉(zhuǎn)發(fā)訪問另一臺服務(wù)器 一次請求,可以在轉(zhuǎn)發(fā)資源間使用request共享數(shù)據(jù) 雖然后臺從/req5轉(zhuǎn)發(fā)到/req6,但是這個只有一次請求 問題:request.getParameter()
request.getParameter(String name)和request.getAttribute(String name);區(qū)別
1.request.getParameter(String name):獲取來自于瀏覽器的數(shù)據(jù)
request.getParameter("username"); 獲取的是鎖哥
2.request.getAttribute(String name)獲取的是服務(wù)器中的代碼:request.setAttibute(String name,Object obj);的數(shù)據(jù)
request.setAttribute("msg","黑馬程序員");
String msg = (String) request.getAttribute("msg");
2.5 request的生命周期
1.何時創(chuàng)建?
瀏覽器第一次訪問tomcat服務(wù)器的時候
2.誰創(chuàng)建?
tomcat創(chuàng)建
3.創(chuàng)建對象做什么?
瀏覽器第一次訪問tomcat服務(wù)器的時候,tomcat創(chuàng)建request對象和response對象,傳遞給servlet中的service方法,然后我們可以在servlet中使用request對象調(diào)用方法獲取請求數(shù)據(jù)(請求行 頭 體),然后處理業(yè)務(wù)邏輯,處理完畢,然后tomcat將響應(yīng)數(shù)據(jù)給瀏覽器,瀏覽器接收到響應(yīng)之后,tomcat立刻銷毀request和response對象。
3.HTTP響應(yīng)詳解(理解)
1.使用抓包查看響應(yīng)報文協(xié)議內(nèi)容
學(xué)習(xí)目標(biāo)
能夠使用抓包查看響應(yīng)報文協(xié)議內(nèi)容
內(nèi)容講解
注意:
http響應(yīng)報文協(xié)議包括:
1.響應(yīng)行
2.響應(yīng)頭
3.響應(yīng)體
響應(yīng)數(shù)據(jù):是服務(wù)器響應(yīng)給瀏覽器
【1】步驟
1.創(chuàng)建html頁面
2.創(chuàng)建servlet
【2】實現(xiàn)
1.創(chuàng)建html頁面
get請求
post請求
2.創(chuàng)建servlet
package com.example.sh.a_http_01;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/getServlet")
public class GetServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//響應(yīng)給瀏覽器數(shù)據(jù)
response.getWriter().print("get....");
}
}
package com.example.sh.a_http_01;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/postServlet")
public class PostServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//響應(yīng)給瀏覽器數(shù)據(jù)
response.getWriter().print("post....");
}
}
【3】抓包結(jié)果
內(nèi)容小結(jié)
? 1.由于瀏覽器的原因,瀏覽器會把請求行和響應(yīng)行信息放在了一起;
? 2.get和post請求的響應(yīng)沒有區(qū)別;
2.HTTP響應(yīng)報文協(xié)議介紹
學(xué)習(xí)目標(biāo)
理解響應(yīng)報文協(xié)議的組成部分
內(nèi)容講解
【1】響應(yīng)行
響應(yīng)行格式:協(xié)議/版本 狀態(tài)碼
如:HTTP/1.1 200 ;
狀態(tài)碼狀態(tài)碼描述說明200OK請求已成功,請求所希望的響應(yīng)頭或數(shù)據(jù)體將隨此響應(yīng)返回。出現(xiàn)此狀態(tài)碼是表示正常狀態(tài)。302Move temporarily重定向,請求的資源臨時從不同的 地址響應(yīng)請求。304Not Modified從瀏覽器緩存中讀取數(shù)據(jù),不從服務(wù)器重新獲取數(shù)據(jù)。例如,用戶第一次從瀏覽器訪問服務(wù)器端圖片資源,以后在訪問該圖片資源的時候就不會再從服務(wù)器上加載而直接到瀏覽器緩存中加載,這樣效率更高。404Not Found請求資源不存在。通常是用戶路徑編寫錯誤,也可能是服務(wù)器資源已刪除。403Forbidden服務(wù)器已經(jīng)理解請求,但是拒絕執(zhí)行它405Method Not Allowed請求行中指定的請求方法不能被用于請求相應(yīng)的資源500Internal Server Error服務(wù)器內(nèi)部錯誤。通常程序拋異常
【2】響應(yīng)頭
響應(yīng)頭也是用的鍵值對key:value,服務(wù)器基于響應(yīng)頭通知瀏覽器的行為。
常見的響應(yīng)頭 :
響應(yīng)頭Key響應(yīng)頭valuelocation指定響應(yīng)的路徑,需要與狀態(tài)碼302配合使用,完成重定向content-Type響應(yīng)正文的類型(MIME類型,屬于服務(wù)器里面的一種類型,例如文件在window系統(tǒng)有自己的類型,.txt .doc .jpg。文件在服務(wù)器中也有自己的類型),同時還可以解決亂碼問題。例如:text/html;charset=UTF-8content-disposition通過瀏覽器以附件形式解析正文,例如:attachment;filename=xx.ziprefresh頁面刷新,例如:3;url=www.itcast.cn //三秒刷新頁面到www.itcast.cn
常見的MIME類型:就是文件在tomcat服務(wù)器中的文件類型:
windows tomcat(MIME類型)
超文本標(biāo)記語言文本 .html text/html ***
xml文檔 .xml text/xml
XHTML文檔 .xhtml application/xhtml+xml
普通文本 .txt text/plain ***
PDF文檔 .pdf application/pdf
Microsoft Word文件 .word application/msword
PNG圖像 .png image/png **
GIF圖形 .gif image/gif
JPEG圖形 .jpeg,.jpg image/jpeg **
......
【3】響應(yīng)體
? 響應(yīng)體,就是服務(wù)器發(fā)送給瀏覽器的數(shù)據(jù)。當(dāng)前瀏覽器向服務(wù)器請求的資源是hello.html,所以服務(wù)器給瀏覽器響應(yīng)的數(shù)據(jù)是一個html頁面。
請求資源路徑:
響應(yīng)結(jié)果:
如果請求是servlet,那么瀏覽器的響應(yīng)體接收到的是servlet響應(yīng)的數(shù)據(jù):
內(nèi)容小結(jié)
1.響應(yīng)行:
? 協(xié)議版本號 狀態(tài)碼 200(一切正常) 404(找不到資源路徑) 500(服務(wù)器報異常) 302(和location一起使用,實現(xiàn)重定向) 304(從瀏覽器緩存中讀取數(shù)據(jù)) 405(服務(wù)器的servlet沒有重寫doGet和doPost方法)
2.響應(yīng)頭:
? location 指定響應(yīng)的路徑
? content-type:告訴瀏覽器文件格式,告訴瀏覽器不要解析html文件(text/plain),解決中文亂碼問題
? refresh 定時刷新
? content-disposition 以附件形式展示圖片等資源
3.響應(yīng)體:
? 服務(wù)器處理的結(jié)果響應(yīng)到瀏覽器中
4.Response對象
1 Response對象介紹
前面講解完Request對象,接下來我們回到剛開始的那張圖:
Request:使用request對象來獲取請求數(shù)據(jù)Response:使用response對象來設(shè)置響應(yīng)數(shù)據(jù)
Reponse的繼承體系和Request的繼承體系也非常相似:
HttpServletResponse response = new ResponseFacade();多態(tài)
介紹完Response的相關(guān)體系結(jié)構(gòu)后,接下來對于Response我們需要學(xué)習(xí)如下內(nèi)容:
Response設(shè)置響應(yīng)數(shù)據(jù)的功能介紹Response完成重定向Response響應(yīng)字符數(shù)據(jù)Response響應(yīng)字節(jié)數(shù)據(jù)
2 Response設(shè)置響應(yīng)數(shù)據(jù)功能介紹
HTTP響應(yīng)數(shù)據(jù)總共分為三部分內(nèi)容,分別是響應(yīng)行、響應(yīng)頭、響應(yīng)體,對于這三部分內(nèi)容的數(shù)據(jù),respone對象都提供了哪些方法來進行設(shè)置?
響應(yīng)行
對于響應(yīng)行,比較常用的就是設(shè)置響應(yīng)狀態(tài)碼:
void setStatus(int sc);
響應(yīng)頭
設(shè)置響應(yīng)頭鍵值對:
void setHeader(String name,String value);
響應(yīng)頭:name的值
location 指定響應(yīng)的路徑
content-type:告訴瀏覽器文件格式,告訴瀏覽器不要解析html文件(text/plain),解決中文亂碼問題 ************
refresh 定時刷新
content-disposition 以附件形式展示圖片等資源
響應(yīng)體
對于響應(yīng)體,是通過字符、字節(jié)輸出流的方式往瀏覽器寫,
獲取字符輸出流:
PrintWriter getWriter();
獲取字節(jié)輸出流
ServletOutputStream getOutputStream();
介紹完這些方法后,后面我們會通過案例把這些方法都用一用,首先先來完成下重定向的功能開發(fā)。
3 Respones請求重定向
Response重定向(redirect):一種資源跳轉(zhuǎn)方式(服務(wù)器外部的)。
(1)瀏覽器發(fā)送請求給服務(wù)器,服務(wù)器中對應(yīng)的資源A接收到請求
(2)資源A現(xiàn)在無法處理該請求,就會給瀏覽器響應(yīng)一個302的狀態(tài)碼+location的一個訪問資源B的路徑
(3)瀏覽器接收到響應(yīng)狀態(tài)碼為302就會重新發(fā)送請求到location對應(yīng)的訪問地址去訪問資源B
(4)資源B接收到請求后進行處理并最終給瀏覽器響應(yīng)結(jié)果,這整個過程就叫重定向
重定向的實現(xiàn)方式:
resp.setStatus(302);設(shè)置響應(yīng)狀態(tài)碼是302
resp.setHeader("location","資源B的訪問路徑");
具體如何來使用,我們先來看下需求:
針對上述需求,具體的實現(xiàn)步驟為:
1.創(chuàng)建一個ResponseDemo1類,接收/resp1的請求,在doGet方法中打印resp1....
2.創(chuàng)建一個ResponseDemo2類,接收/resp2的請求,在doGet方法中打印resp2....
3.在ResponseDemo1的方法中使用
? response.setStatus(302);
? response.setHeader(“Location”,“/request-demo/resp2”) 來給前端響應(yīng)結(jié)果數(shù)據(jù)
4.啟動測試
(1)創(chuàng)建ResponseDemo1類
@WebServlet("/resp1")
public class ResponseDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("resp1....");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
(2)創(chuàng)建ResponseDemo2類
@WebServlet("/resp2")
public class ResponseDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("resp2....");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
(3)在ResponseDemo1的doGet方法中給前端響應(yīng)數(shù)據(jù)
@WebServlet("/resp1")
public class ResponseDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("resp1....");
//重定向
//1.設(shè)置響應(yīng)狀態(tài)碼 302
response.setStatus(302);
//2. 設(shè)置響應(yīng)頭 Location
response.setHeader("Location","/request-demo/resp2");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
(4)啟動測試
訪問http://localhost:8080/request-demo/resp1,就可以在控制臺看到如下內(nèi)容:
說明/resp1和/resp2都被訪問到了。到這重定向就已經(jīng)完成了。
雖然功能已經(jīng)實現(xiàn),但是從設(shè)置重定向的兩行代碼來看,會發(fā)現(xiàn)除了重定向的地址不一樣,其他的內(nèi)容都是一模一樣,所以resposne對象給我們提供了簡化的編寫方式為:
resposne.sendRedirect("/request-demo/resp2")
所以第3步中的代碼就可以簡化為:
@WebServlet("/resp1")
public class ResponseDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("resp1....");
//重定向
resposne.sendRedirect("/request-demo/resp2");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
重定向的特點
瀏覽器地址欄路徑發(fā)送變化 當(dāng)進行重定向訪問的時候,由于是由瀏覽器發(fā)送的兩次請求,所以地址會發(fā)生變化 可以重定向到任何位置的資源(服務(wù)內(nèi)容、外部均可) 因為第一次響應(yīng)結(jié)果中包含了瀏覽器下次要跳轉(zhuǎn)的路徑,所以這個路徑是可以任意位置資源。 兩次請求,不能在多個資源使用request共享數(shù)據(jù) 因為瀏覽器發(fā)送了兩次請求,是兩個不同的request對象,就無法通過request對象進行共享數(shù)據(jù)
介紹完請求重定向和請求轉(zhuǎn)發(fā)以后,接下來需要把這兩個放在一塊對比下:
以后到底用哪個,還是需要根據(jù)具體的業(yè)務(wù)來決定。
# 如果需要在資源之間傳遞共享request數(shù)據(jù),使用請求轉(zhuǎn)發(fā), 否則就用重定向
4 路徑問題
問題1:轉(zhuǎn)發(fā)的時候路徑上沒有加/request-demo而重定向加了,那么到底什么時候需要加,什么時候不需要加呢?
其實判斷的依據(jù)很簡單,只需要記住下面的規(guī)則即可:
瀏覽器使用:需要加虛擬目錄(項目訪問路徑)服務(wù)端使用:不需要加虛擬目錄
對于轉(zhuǎn)發(fā)來說,因為是在服務(wù)端進行的,所以不需要加虛擬目錄
對于重定向來說,路徑最終是由瀏覽器來發(fā)送請求,就需要添加虛擬目錄。
掌握了這個規(guī)則,接下來就通過一些練習(xí)來強化下知識的學(xué)習(xí):
答案:
1.超鏈接,從瀏覽器發(fā)送,需要加
2.表單,從瀏覽器發(fā)送,需要加
3.重定向,是由瀏覽器進行跳轉(zhuǎn),需要加。
4.轉(zhuǎn)發(fā),是從服務(wù)器內(nèi)部跳轉(zhuǎn),不需要加
5 Response響應(yīng)字符數(shù)據(jù)
要想將字符數(shù)據(jù)寫回到瀏覽器,我們需要兩個步驟:
通過Response對象獲取字符輸出流: PrintWriter writer = resp.getWriter(); 由于獲取打印字符流是根據(jù)response對象獲取的,所以向瀏覽器打印輸出 通過字符輸出流寫數(shù)據(jù): writer.write(“aaa”);
接下來,我們實現(xiàn)通過些案例把響應(yīng)字符數(shù)據(jù)給實際應(yīng)用下:
返回一個簡單的字符串a(chǎn)aa
/**
* 響應(yīng)字符數(shù)據(jù):設(shè)置字符數(shù)據(jù)的響應(yīng)體
*/
@WebServlet("/resp3")
public class ResponseDemo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
//1. 獲取字符輸出流
PrintWriter writer = response.getWriter();
writer.write("aaa");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
返回一串html字符串,并且能被瀏覽器解析
PrintWriter writer = response.getWriter();
//content-type,告訴瀏覽器返回的數(shù)據(jù)類型是HTML類型數(shù)據(jù),這樣瀏覽器才會解析HTML標(biāo)簽
response.setHeader("content-type","text/html");
writer.write("
aaa
");==注意:==一次請求響應(yīng)結(jié)束后,response對象就會被銷毀掉,所以不要手動關(guān)閉流。
返回一個中文的字符串你好,需要注意設(shè)置響應(yīng)數(shù)據(jù)的編碼為utf-8
//設(shè)置響應(yīng)的數(shù)據(jù)格式及數(shù)據(jù)的編碼
response.setContentType("text/html;charset=utf-8");
writer.write("你好");
6 Response響應(yīng)字節(jié)數(shù)據(jù)
要想將字節(jié)數(shù)據(jù)寫回到瀏覽器,我們需要兩個步驟:
通過Response對象獲取字節(jié)輸出流:ServletOutputStream outputStream = resp.getOutputStream(); 通過字節(jié)輸出流寫數(shù)據(jù): outputStream.write(字節(jié)數(shù)據(jù));
接下來,我們實現(xiàn)通過些案例把響應(yīng)字節(jié)數(shù)據(jù)給實際應(yīng)用下:
返回一個圖片文件到瀏覽器
/**
* 響應(yīng)字節(jié)數(shù)據(jù):設(shè)置字節(jié)數(shù)據(jù)的響應(yīng)體
*/
@WebServlet("/resp4")
public class ResponseDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 讀取文件
FileInputStream fis = new FileInputStream("D:\\abc\\柳巖.jpg");
//2. 獲取response字節(jié)輸出流
ServletOutputStream os = response.getOutputStream();
//3. 完成流的copy
byte[] buff = new byte[1024];
int len = 0;
while ((len = fis.read(buff))!= -1){
os.write(buff,0,len);
}
fis.close();
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
上述代碼中,對于流的copy的代碼還是比較復(fù)雜的,所以我們可以使用別人提供好的方法來簡化代碼的開發(fā),具體的步驟是:
(1)pom.xml添加依賴
(2)調(diào)用工具類方法
//fis:輸入流
//os:輸出流
IOUtils.copy(fis,os);
優(yōu)化后的代碼:
/**
* 響應(yīng)字節(jié)數(shù)據(jù):設(shè)置字節(jié)數(shù)據(jù)的響應(yīng)體
*/
@WebServlet("/resp4")
public class ResponseDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 讀取文件
FileInputStream fis = new FileInputStream("d://a.jpg");
//2. 獲取response字節(jié)輸出流
ServletOutputStream os = response.getOutputStream();
//3. 完成流的copy
IOUtils.copy(fis,os);
fis.close();
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
回顧
# http協(xié)議
1. 超文本傳輸協(xié)議
目前web領(lǐng)域最常用的協(xié)議
2. 特點
1). 基于tcp, 面向連接, 安全
2). 基于請求-響應(yīng)模型: 一次請求對應(yīng)一次響應(yīng)
3). 無狀態(tài),每次請求都相互獨立,無法共享數(shù)據(jù)
I. 優(yōu)點 : 速度快
II. 缺點 : 多次請求無法共享數(shù)據(jù),需要其他技術(shù)彌補(會話技術(shù))
4). http協(xié)議默認(rèn)端口80(可以省略)
3. 內(nèi)容
1). 請求數(shù)據(jù)(報文)
I. 請求行
請求方式, 請求地址, 協(xié)議
II. 請求頭
key : value
III. 請求體
用于post請求存放請求參數(shù)
get請求沒有請求體,get請求參數(shù)在請求行中(在請求地址里)
2). 響應(yīng)數(shù)據(jù)
I. 響應(yīng)行
協(xié)議, 狀態(tài)碼(status code)
200 : 請求成功
404 : not found (請求地址錯誤)
500 : 服務(wù)器錯誤
302 : 重定向
II. 響應(yīng)頭
key : value
III. 響應(yīng)體
響應(yīng)數(shù)據(jù)的正文
tomcat
web服務(wù)器,apache出品的開源免費的軟件 能夠解析http協(xié)議 默認(rèn)占用8080端口基本使用 1). 啟動 bin/startup.bat 2). 配置 conf/server.xml(修改服務(wù)器設(shè)置,包括端口) conf/web.xml(修改部署項目的設(shè)置) 3). 部署 webapps目錄下放war包 4). 查看日志 logs目錄idea中集成tomcat
# Servlet
1. 服務(wù)器小程序, 需要運行web服務(wù)器上
在java中的體現(xiàn),是javaee的一種規(guī)范,類型為接口
2. 執(zhí)行原理
1). servlet對象是web服務(wù)器創(chuàng)建的
2). servlet的方法是web服務(wù)器調(diào)用的
3. 生命周期方法
1). init
2). service
3). destroy
4). 啟動加載 : loadOnStartup >= 0
4. urlPattern
1). 精準(zhǔn)匹配
2). 目錄匹配
3). 擴展名匹配
4). 缺省匹配
5. servlet的配置方式
1). 注解配置 WebServlet
2). xml配置 (web.xml)
總結(jié)
# Request
1. 是什么: 表示請求數(shù)據(jù), 瀏覽器發(fā)送給服務(wù)器的是數(shù)據(jù)
2. 為什么:
1). 見名知意
2). request對象是由tomcat創(chuàng)建的,tomcat會解析瀏覽器發(fā)送過來的數(shù)據(jù),并封裝到request對象中
3. 怎么辦:
1). 我們(servlet開發(fā)者)可以通過request對象獲取請求數(shù)據(jù)
I. 請求行
a. 內(nèi)容 : 請求方式, 請求地址, 請求協(xié)議
b. 請求方式: get/post
c. get的請求參數(shù)在請求地址中
d. API :
II. 請求頭
a. 格式 key : value
b. 請求頭 user-agent (用戶代理: 用戶操作系統(tǒng)和瀏覽器)
c. API
String value = request.getHeader("user-agent");
III. 請求體
a. post請求參數(shù)在這里,get沒有請求體
IV. 通用的獲取請求參數(shù)的API
V. 請求參數(shù)中文亂碼
a. 現(xiàn)象
tomcat7
tomcat8
b. 原因
c. 解決方案
2). 我們可以通過request對象實現(xiàn)請求轉(zhuǎn)發(fā)
I. 是服務(wù)器內(nèi)部的一種資源跳轉(zhuǎn)方式(資源包括html,Servlet)
II. 特點
a. 就只有一次請求,可以通過request域?qū)ο蠊蚕頂?shù)據(jù)
b. 地址欄不會變化
c. 只能跳轉(zhuǎn)服務(wù)器內(nèi)部的其他資源
III. API
# Response
1. 是什么 : 表示響應(yīng)數(shù)據(jù), 服務(wù)器發(fā)送給瀏覽器的數(shù)據(jù)
2. 為什么 :
response對象是tomcat創(chuàng)建的
3. 怎么辦
1). 我們可以通過response設(shè)置響應(yīng)數(shù)據(jù)
I. 響應(yīng)行
a. 內(nèi)容: 協(xié)議,狀態(tài)碼
b. 常見狀態(tài)碼(status code)
200
302
404
500
c. 通常情況下,tomcat會設(shè)置狀態(tài)碼,只有重定向才需要手動設(shè)置
II. 響應(yīng)頭
a. 格式 key:value
b. location : 重定向時指定資源位置的
c. content-type : 可以用來防止響應(yīng)體中文字符亂碼的
III. 響應(yīng)體
a. 字符輸出流
b. 字節(jié)輸出流
2). 重定向功能
I. 是一種資源跳轉(zhuǎn)方式
II. 特點:
a. 起碼兩次請求,不可以通過request域?qū)ο蠊蚕頂?shù)據(jù)
b. 地址欄會發(fā)生變化
c. 可以實現(xiàn)任意位置資源跳轉(zhuǎn),無論服務(wù)器內(nèi)部還是外部
3). 響應(yīng)字符/字節(jié)數(shù)據(jù)
0.Servlet : 屬于一個接口,動態(tài)資源,如果瀏覽器想訪問服務(wù)器中的動態(tài)資源,那么需要自定義類直接或者間接實現(xiàn)Servlet接口
1)自定義類實現(xiàn)Servlet接口===自定義類中要實現(xiàn)Servlet接口中的全部的抽象方法
2)自定義類繼承GenericServlet====只需要重寫ervice方法
3)自定義類繼承HttpServlet===》需要重寫doGet doPost方法 ******
1.request:處理瀏覽器向服務(wù)器發(fā)送請求的數(shù)據(jù)的(請求行 請求頭 請求體)
2.request繼承體系:
ServletRequest(父接口)==== HttpServletRequest(子接口) === RequestFacade(實現(xiàn)類,tomcat定義)
3.獲取請求參數(shù):
1)Map
2)String[] getParameterValues(String name) 獲取指定的name的值,是一個數(shù)組
3)String getParameter(String name)獲取指定的name的值,一個值
4.處理請求亂碼:
1)
URLEncoder.encode(字符串,編碼表); 編碼
URLDecoder.decode(字符串,編碼表); 解碼
2)String類
Byte[] 字符串.getBytes("編碼表");編碼
String s = new String(字節(jié)數(shù)組,編碼表);解碼
3)
只是解決請求體亂碼的,即解決post
request.setCharacterEncoding(編碼表);
5.請求轉(zhuǎn)發(fā):屬于服務(wù)器內(nèi)部跳轉(zhuǎn)。地址不發(fā)生改變,只有一次請求一次響應(yīng),可以共享request數(shù)據(jù)
request.getRequestDispatcher("/loginServlet").forward(request,response); 只要通過瀏覽器訪問必須加虛擬路徑day03
補充:絕對路徑:
1)http://localhost:8080/day03/loginServlet
2)/day03/loginServlet 省略三要素,必須加/
6.response:處理服務(wù)器響應(yīng)數(shù)據(jù)(響應(yīng)行 響應(yīng)頭 響應(yīng)體)給瀏覽器
7.繼承體系:
ServletResponse(父接口)==== HttpServletResponse(子接口) === ResponseFacade(實現(xiàn)類,tomcat定義)
8.響應(yīng)行:狀態(tài)碼 200 404 500 302 304(從瀏覽器緩存中獲取)
9.設(shè)置響應(yīng)頭:response.setHeader("content-type","text/html;charset=utf-8") MIME:文件在tomcat服務(wù)器中的類型
response.setContentType("text/html;charset=utf-8")//告知瀏覽器文件的MIME類型并解決響應(yīng)亂碼問題
10.響應(yīng)體:
1)響應(yīng)字符數(shù)據(jù):response.getWriter().print();
2) 響應(yīng)字節(jié)數(shù)據(jù):response.getOutputStream()響應(yīng)字節(jié)數(shù)據(jù)
11.重定向:實現(xiàn)服務(wù)器外部跳轉(zhuǎn),多次請求和響應(yīng),不能共享request數(shù)據(jù),地址發(fā)生改變
1)
response.setStatus(302);
response.setHeader("location","/day03/loginServlet");必須加虛擬路徑,因為屬于服務(wù)器外部,經(jīng)過瀏覽器再次向服務(wù)器發(fā)送請求
2)
response.sendRedirect("/day03/loginServlet");
柚子快報激活碼778899分享:Servlet中的請求與響應(yīng)
好文閱讀
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。