1. request和response的實(shí)現(xiàn)結(jié)構(gòu)
ServletRequest
接口是所有Request類的父類菌瘫,HttpServletRequest
是用于http請求的Request接口鸡号。這兩者都是接口愧口,不進(jìn)行具體的實(shí)現(xiàn)气嫁。ServletRequest
和HttpServletRequest
都屬于servelt-api.jar
,為J2EE的標(biāo)準(zhǔn)規(guī)范膜楷。所有廠商要想實(shí)現(xiàn)具體的Request和Response旭咽,無論怎么實(shí)現(xiàn)都沒關(guān)系,但是必須遵守J2EE標(biāo)準(zhǔn)規(guī)范赌厅。
catalina.jar
就是tomcat apache的實(shí)現(xiàn)方式穷绵。打開tomcat的lib目錄就可以看到
catalina.jar
:2. HttpServletRequest
HttpServletRequest 接口繼承自 ServletRequest 接口,其主要作用是封裝 HTTP 請求消息特愿。由于 HTTP 請求消息分為請求行仲墨、請求消息頭和請求消息體三部分。因此揍障,在 HttpServletRequest 接口中定義了獲取請求行
目养、請求頭
和請求消息體
的相關(guān)方法。
2.1 請求行
請求消息的請求行中包含請求方法毒嫡、請求資源名癌蚁、請求路徑等信息,為了獲取這些信息兜畸,
HttpServletRequest 接口定義了一系列方法:
方法聲明 | 功能描述 |
---|---|
String getMethod() | 該方法用于獲取 HTTP 請求消息中的請求方式(如 GET努释、POST 等) |
String getRequestURI() | 該方法用于獲取請求行中的資源名稱部分即位于 URL 的主機(jī)和端門之后、參數(shù)部分之前的部分 |
String getQueryString() | 該方法用于獲取請求行中的參數(shù)部分咬摇,也就是資源路徑后問號(伐蒂?)以后的所有內(nèi)容 |
String getContextPath() | 該方法用于獲取請求 URL 中屬于 Web 應(yīng)用程序的路徑,這個路徑以 / 開頭肛鹏,表示相對于整個 Web 站點(diǎn)的根目錄逸邦,路徑結(jié)尾不含 /恩沛。如果請求 URL 屬于 Web 站點(diǎn)的根目錄,那么返回結(jié)果為空字符串("") |
String getServletPath() | 該方法用于獲取 Servlet 的名稱或 Servlet 所映射的路徑 |
String getRemoteAddr() | 該方法用于獲取請求客戶端的 IP 地址缕减,其格式類似于 192.168.0.3 |
String getRemoteHost() | 該方法用于獲取請求客戶端的完整主機(jī)名雷客,其格式類似于 pcl.mengma.com。需要注意的是烛卧,如果無法解析出客戶機(jī)的完整主機(jī)名佛纫,那么該方法將會返回客戶端的 IP 地址 |
int getRemotePort() | 該方法用于獲取請求客戶端網(wǎng)絡(luò)連接的端口號 |
String getLocaIAddr() | 該方法用于獲取 Web 服務(wù)器上接收當(dāng)前請求網(wǎng)絡(luò)連接的 IP 地址 |
String getLocalName() | 該方法用于獲取 Web 服務(wù)器上接收當(dāng)前網(wǎng)絡(luò)連接 IP 所對應(yīng)的主機(jī)名 |
int getLocalPort() | 該方法用于獲取 Web 服務(wù)器上接收當(dāng)前網(wǎng)絡(luò)連接的端口號 |
String getServerName() | 該方法用于獲取當(dāng)前請求所指向的主機(jī)名,即 HTTP 請求消息中 Host 頭字段所對應(yīng)的主機(jī)名部分 |
int gctServcrPort() | 該方法用于獲取當(dāng)前請求所連接的服務(wù)器端口號总放,即 HTTP 請求消息中 Host 頭字段所對應(yīng)的端口號部分 |
StringBuffcr getRequestURL() | 該方法用于獲取客戶端發(fā)出請求時的完整 URL,包括協(xié)議好爬、服務(wù)器名局雄、端口號、 資源路徑等信息存炮,但不包括后面的査詢參數(shù)部分炬搭。注意,getRequcstURL() 方法返冋的結(jié)果是 StringBuffer 類型穆桂,而不是 String 類型宫盔,這樣更便于對結(jié)果進(jìn)行修改 |
2.2 請求頭
當(dāng)瀏覽器發(fā)送 Servlet 請求時,需要通過請求消息頭向服務(wù)器傳遞附加信息享完,例如灼芭,客戶端可以接收的數(shù)據(jù)類型、壓縮方式般又、語言等彼绷。為此,在 HttpServletRequest 接口中定義了一系列用于獲取 HTTP 請求頭字段的方法
方法聲明 | 功能描述 |
---|---|
String getHeader(String name) | 該方法用于獲取一個指定頭字段的值茴迁,如果請求消息中沒有包含指定的頭字段寄悯,則 getHeader() 方法返回 null;如果請求消息中包含多個指定名稱的頭字段堕义,則 getHeader() 方法返回其中第一個頭字段的值 |
Enumeration getHeaders(String name) | 該方法返回一個 Enumeration 集合對象猜旬,該集合對象由請求消息中出現(xiàn)的某個指定名稱的所有頭字段值組成。在多數(shù)情況下倦卖,一個頭字段名在請求消息中只出現(xiàn)一次洒擦,但有時可能會出現(xiàn)多次 |
Enumeration getHeaderNames() | 該方法用于獲取一個包含所有請求頭字段的 Enumeration 對象 |
int getIntHeader(String name) | 該方法用于獲取指定名稱的頭字段,并且將其值轉(zhuǎn)為 int 類型糖耸。需要注意的是秘遏,如果指定名稱的頭字段不存在,則返回值為 -1嘉竟;如果獲取到的頭字段的值不能轉(zhuǎn)為 int 類型邦危,則將發(fā)生 NumberFormatException 異常 |
long getDateHeader(String name) | 該方法用于獲取指定頭字段的值洋侨,并將其按 GMT 時間格式轉(zhuǎn)換為一個代表日期/時間的長整數(shù),該長整數(shù)是自 1970 年 1 月 1 日 0 時 0 分 0 秒算起的以毫秒為單位的時間值 |
String getContentType() | 該方法用于獲取 Content-Type 頭字段的值倦蚪,結(jié)果為 String 類型 |
int getContentLength() | 該方法用于獲取 Content-Length 頭字段的值希坚,結(jié)果為 int 類型 |
String getCharacterEncoding() | 該方法用于返回請求消息的實(shí)體部分的字符集編碼,通常是從 Content-Type 頭字段中進(jìn)行提取陵且,結(jié)果為 String 類型 |
2.3 獲取請求參數(shù)及對象
2.3.1 獲取請求參數(shù)
為了方便獲取表單中的請求參數(shù)裁僧,在 HttpServletRequest 接口的父類 ServletRequest
中定義了一系列獲取請求參數(shù)的方法
方法聲明 | 功能描述 |
---|---|
String getParameter(String name) | 該方法用于獲取某個指定名稱的參數(shù)值。如果請求消息中沒有包含指定名稱的參數(shù)慕购,則 getParameter() 方法返回 null聊疲。如果指定名稱的參數(shù)存在但沒有設(shè)置值,則返回一個空串沪悲。 如果請求消息中包含多個該指定名稱的參數(shù)获洲,則 getParameter() 方法返回第一個出現(xiàn)的參數(shù)值。 |
String [] getParameterValues (String name) | HTTP 請求消息中可以有多個相同名稱的參數(shù)(通常由一個包含多個同名的字段元素的 form 表單生成)殿如,如果要獲得 HTTP 請求消息中的同一個參數(shù)名所對應(yīng)的所有參數(shù)值贡珊,那么就應(yīng)該使用 getParameterValues() 方法,該方法用于返回一個 String 類型的數(shù)組涉馁。 |
Enumeration getParameterNames() | 方法用于返回一個包含請求消息中所有參數(shù)名的 Enumeration 對象门岔,在此基礎(chǔ)上,可以對請求消息中的所有參數(shù)進(jìn)行遍歷處理烤送。 |
Map getParameterMap() | getParameterMap() 方法用于將請求消息中的所有參數(shù)名和值裝入一個 Map 對象中返回寒随。 |
2.3.1 獲取請求對象
Request 對象不僅可以獲取一系列數(shù)據(jù),還可以通過屬性傳遞數(shù)據(jù)胯努。ServletRequest 接口中定義了一系列操作屬性的方法牢裳。
方法聲明 | 功能描述 |
---|---|
void setAttribute(String name, Object o) | 方法用于將一個對象與一個名稱關(guān)聯(lián)后存儲到 ServletRequest 對象中 |
Object getAttribute(String name) | 從 ServletRequest 對象中返回指定名稱的屬性對象 |
void removeAttribute(String name); | 該方法用于從 ServletRequest 對象中刪除指定名稱的屬性 |
3. HttpServletResponse
HttpServletResponse 接口繼承自 ServletResponse 接口,主要用于封裝 HTTP 響應(yīng)消息叶沛。由于 HTTP 響應(yīng)消息分為狀態(tài)行蒲讯、響應(yīng)消息頭、消息體三部分灰署。因此判帮,在 HttpServletResponse 接口中定義了向客戶端發(fā)送響應(yīng)狀態(tài)碼
、響應(yīng)消息頭
溉箕、響應(yīng)消息體
的方法
3.1 響應(yīng)狀態(tài)碼
當(dāng) Servlet 向客戶端回送響應(yīng)消息時晦墙,需要在響應(yīng)消息中設(shè)置狀態(tài)碼。因此肴茄,HttpServletResponse 接口定義了兩個發(fā)送狀態(tài)碼的方法晌畅。
1)setStatus(int status)
方法
該方法用于設(shè)置 HTTP 響應(yīng)消息的狀態(tài)碼,并生成響應(yīng)狀態(tài)行寡痰。由于響應(yīng)狀態(tài)行中的狀態(tài)描述信息直接與狀態(tài)碼相關(guān)抗楔,而 HTTP 版本由服務(wù)器確定棋凳,因此,只要通過 setStatus(int status)方法設(shè)置了狀態(tài)碼连躏,即可實(shí)現(xiàn)狀態(tài)行的發(fā)送剩岳。需要注意的是,在正常情況下入热,Web 服務(wù)器會默認(rèn)產(chǎn)生一個狀態(tài)碼為 200 的狀態(tài)行拍棕。
2)sendError(int sc)
方法
該方法用于發(fā)送表示錯誤信息的狀態(tài)碼。例如勺良,404 狀態(tài)碼表示找不到客戶端請求的資源绰播。response 對象提供了兩個重載的 sendError(int sc)方法,具體如下:
public void sendError(int code) throws java.io.IOException
public void sendError(int code,String message)throws java.io.IOException
在上面重載的兩個方法中尚困,第一個方法只發(fā)送錯誤信息的狀態(tài)碼幅垮,而第二個方法除了發(fā)送狀態(tài)碼以外,還可以增加一條用于提示說明的文本信息尾组,該文本信息將出現(xiàn)在發(fā)送給客戶端的正文內(nèi)容中。
3.2 響應(yīng)消息體body
由于在 HTTP 響應(yīng)消息中示弓,大量的數(shù)據(jù)都是通過響應(yīng)消息體傳遞的讳侨,因此,ServletResponse 遵循以 I/O 流傳遞大量數(shù)據(jù)的設(shè)計理念奏属。在發(fā)送響應(yīng)消息體時跨跨,定義了兩個與輸出流相關(guān)的方法。
1)getOutputStream()
方法
該方法所獲取的字節(jié)輸出流對象為 ServletOutputStream 類型囱皿。由于 ServletOutputStream是OutputStream 的子類勇婴,它可以直接輸出字節(jié)數(shù)組中的二進(jìn)制數(shù)據(jù)。因此嘱腥,要想輸出二進(jìn)制格式的響應(yīng)正文耕渴,就需要使用 getOutputStream() 方法。
2)getWriter()
方法
該方法所獲取的字符輸出流對象為 PrintWriter 類型齿兔。由于 PrintWriter 類型的對象可以直接輸出字符文本內(nèi)容橱脸,因此,要想輸出內(nèi)容全部為字符文本的網(wǎng)頁文檔分苇,則需要使用 getWriter() 方法添诉。
注意:雖然 response 對象的 getOutputStream() 和 getWriter() 方法都可以發(fā)送響應(yīng)消息體,但是医寿,它們之間互相排斥栏赴,不可同時使用,否則會發(fā)生 IllegalStateException 異常靖秩。
3.3 響應(yīng)消息頭header
Servlet 向客戶端發(fā)送的響應(yīng)消息中包含響應(yīng)頭字段须眷,由于 HTTP 協(xié)議的響應(yīng)頭字段有很多種竖瘾,因此,HttpServletResponse 接口定義了一系列設(shè)置 HTTP 響應(yīng)頭字段的方法柒爸,如表 2 所示准浴。
方法聲明 | 功能描述 |
---|---|
void addHeader/setHeader(String name,String value) | 這兩個方法都是用于設(shè)置 HTTP 協(xié)議的響應(yīng)頭字段。其中捎稚,參數(shù) name 用于指定響應(yīng)頭字段的名稱乐横,參數(shù) value 用于指定響 應(yīng)頭字段的值。不同的是今野,addHeader() 方法可以增加同名的響應(yīng)頭字段葡公,而 setHeader() 方法則會覆蓋同名的頭字段 |
void addIntHeader/setIntHeader(String name,int value) | 這兩個方法專門用于設(shè)置包含整數(shù)值的響應(yīng)頭,避免了使用 addHeader() 與 setHeader() 方法時需要將 int 類型的設(shè)置值轉(zhuǎn)換為 String 類型的麻煩 |
void setContentType(String type) | 該方法用于設(shè)置 Servlet 輸出內(nèi)容的 MIME 類型条霜,對于 HTTP 協(xié)議來說催什,就是設(shè)置 Content-Type 響應(yīng)頭字段的值。例如宰睡,如果發(fā)送到客戶端的內(nèi)容是 jpeg 格式的圖像數(shù)據(jù),就需要將響應(yīng)頭字段的類型設(shè)置為 image/jpeg蒲凶。需要注意的是,如果響應(yīng)的內(nèi)容為文本拆内,setContentType() 方法還可以設(shè)置字符編碼旋圆,如 text/html;charset = UTF-8 |
void setLocale (Locale loc) | 該方法用于設(shè)置響應(yīng)消息的本地化信息。對 HTTP 來說麸恍,就是設(shè)置 Content-Language 響應(yīng)頭字段和 Content-Type 頭字段中的字符集編碼部分灵巧。需要注意的是,如果 HTTP 消息沒有設(shè)置 Content-Type 頭字段抹沪,則 setLocale() 方法設(shè)置的字符集編碼不會出現(xiàn)在 HTTP 消息的響應(yīng)頭中刻肄,如果調(diào)用 setCharacterEncoding() 或 setContentType() 方法指定了響應(yīng)內(nèi) 容的字符集編碼,則 setLocale() 方法將不再具有指定字符集編碼的功能 |
void setCharacterEncoding(String charset) | 該方法用于設(shè)置輸出內(nèi)容使用的字符編碼融欧,對 HTTP 協(xié)議來說敏弃,就是設(shè)置 Content-Type 頭字段中的字符集編碼部分。如果沒有設(shè)置 Content-Type 頭字段蹬癌,則 setCharacterEncoding 方法設(shè) 置的字符集編碼不會出現(xiàn)在 HTTP 消息的響應(yīng)頭中权她。setCharacterEncoding() 方法比 setContentType() 和 setLocale() 方法的優(yōu)先權(quán)高,它的設(shè)置結(jié)果將覆蓋 setContentType() 和 setLocale() 方法所設(shè)置的字符碼表 |
需要注意的是逝薪,在一系列方法中隅要,addHeader()、setHeader()董济、addIntHeader()步清、setIntHeader() 方法都用于設(shè)置各種頭字段,而 setContetType()、setLoacale() 和 setCharacterEncoding() 方法用于設(shè)置字符編碼廓啊,這些設(shè)置字符編碼的方法可以有效解決亂碼問題欢搜。
4 請求轉(zhuǎn)發(fā)與重定向
轉(zhuǎn)發(fā)是服務(wù)器行為,重定向是客戶端行為谴轮。
4.1 請求轉(zhuǎn)發(fā)forward
是服務(wù)器請求資源,服務(wù)器直接訪問目標(biāo)地址的URL,把那個URL的響應(yīng)內(nèi)容讀取過來,然后把這些內(nèi)容再發(fā)給瀏覽器.瀏覽器根本不知道服務(wù)器發(fā)送的內(nèi)容從哪里來的,因?yàn)檫@個跳轉(zhuǎn)過程實(shí)在服務(wù)器實(shí)現(xiàn)的炒瘟,并不是在客戶端實(shí)現(xiàn)的所以客戶端并不知道這個跳轉(zhuǎn)動作,所以它的地址欄還是原來的地址.
//Servlet里處理get請求的方法
public void doGet(HttpServletRequest request , HttpServletResponse response){
//獲取請求轉(zhuǎn)發(fā)器對象第步,該轉(zhuǎn)發(fā)器的指向通過getRequestDisPatcher()的參數(shù)設(shè)置
RequestDispatcher requestDispatcher =request.getRequestDispatcher("資源的URL");
//調(diào)用forward()方法疮装,轉(zhuǎn)發(fā)請求
requestDispatcher.forward(request,response);
}
4.2 重定向redirect
是服務(wù)端根據(jù)邏輯,發(fā)送一個狀態(tài)碼,告訴瀏覽器重新去請求那個地址.所以地址欄顯示的是新的URL.
//Servlet中處理get請求的方法
public void doGet(HttpServletRequest request,HttpServletResponse response){
//請求重定向到另外的資源
response.sendRedirect("資源的URL");
}
4.3 區(qū)別
- 重定向是兩次請求,轉(zhuǎn)發(fā)是一次請求粘都,因此轉(zhuǎn)發(fā)的速度要快于重定向
- 重定向之后地址欄上的地址會發(fā)生變化廓推,變化成第二次請求的地址,轉(zhuǎn)發(fā)之后地址欄上的地址不會變化翩隧,還是第一次請求的地址
- 轉(zhuǎn)發(fā)是服務(wù)器行為樊展,重定向是客戶端行為。重定向時瀏覽器上的網(wǎng)址改變 ,轉(zhuǎn)發(fā)是瀏覽器上的網(wǎng)址不變
- 重定向是兩次request堆生,轉(zhuǎn)發(fā)只有一次請求
- 重定向時的網(wǎng)址可以是任何網(wǎng)址专缠,轉(zhuǎn)發(fā)的網(wǎng)址必須是本站點(diǎn)的網(wǎng)址.
- 重定向時的根目錄為webapps,請求轉(zhuǎn)發(fā)的根目錄為本項目的根目錄