| 
 一:概述 Web服務(wù)器收到客戶(hù)端的http請(qǐng)求,會(huì)針對(duì)每一次請(qǐng)求,分別創(chuàng)建一個(gè)用于代表請(qǐng)求的request對(duì)象、和代表響應(yīng)的response對(duì)象
 一、Response對(duì)象
 1.Resonse的繼承結(jié)構(gòu):
 ServletResponse--HttpServletResponse
 2.Response代表響應(yīng),于是響應(yīng)消息中的 狀態(tài)碼、響應(yīng)頭、實(shí)體內(nèi)容都可以由它進(jìn)行操作,由此引伸出如下實(shí)驗(yàn):
 3.利用Response輸出數(shù)據(jù)到客戶(hù)端
 response.getOutputStream().write('中文'.getBytes())輸出數(shù)據(jù),這是一個(gè)字節(jié)流,寫(xiě)入內(nèi)存使用什么編碼,輸出就使用什么編碼,而瀏覽器默認(rèn)用平臺(tái)字節(jié)碼打開(kāi)服務(wù)器發(fā)送的數(shù)據(jù),如果服務(wù)器端使用了非平臺(tái)碼去輸出字符的字節(jié)數(shù)據(jù)就需要明確的指定瀏覽器編碼時(shí)所用的碼表,以防止亂碼問(wèn)題。
        response.addHeader('Content-type','text/html;charset=gb2312')response.getWriter().write(“中文”);輸出數(shù)據(jù),這是一個(gè)字符流,response會(huì)將此字符進(jìn)行轉(zhuǎn)碼操作后輸出到瀏覽器,這個(gè)過(guò)程默認(rèn)使用ISO8859-1碼表,而ISO8859-1中沒(méi)有中文,于是轉(zhuǎn)碼過(guò)程中用?代替了中文,導(dǎo)致亂碼問(wèn)題??梢灾付╮esponse在轉(zhuǎn)碼過(guò)程中使用的目標(biāo)碼表,防止亂碼。response.setCharacterEncoding('gb2312');
 其實(shí)response還提供了setContentType('text/html;charset=gb2312')方法,此方法會(huì)設(shè)置content-type響應(yīng)頭,通知瀏覽器打開(kāi)的碼表,同時(shí)設(shè)置response的轉(zhuǎn)碼時(shí)使的用碼表,從而一行代碼解決亂碼。
 4.利用Response 設(shè)置 content-disposition頭實(shí)現(xiàn)文件下載
 設(shè)置響應(yīng)頭content-disposition為“attachment;filename=xxx.xxx”
 利用流將文件讀取進(jìn)來(lái),再利用Response獲取響應(yīng)流輸出
 如果文件名為中,一定要進(jìn)行URL編碼,編碼所用的碼表一定要是UTF-8
 5.refresh頭控制定時(shí)刷新
 設(shè)置響應(yīng)頭Refresh為一個(gè)數(shù)值,指定多少秒后刷新當(dāng)前頁(yè)面
 設(shè)置響應(yīng)頭Refresh為 3;url=/Day05/index.jsp,指定多少秒后刷新到哪個(gè)頁(yè)面
 可以用來(lái)實(shí)現(xiàn)注冊(cè)后“注冊(cè)成功,3秒后跳轉(zhuǎn)到主頁(yè)”的功能
 在HTML可以利用標(biāo)簽?zāi)M響應(yīng)頭的功能。
       eg:6.利用response設(shè)置expires、Cache-Control、Pragma實(shí)現(xiàn)瀏覽器是否緩存資源,這三個(gè)頭都可以實(shí)現(xiàn),但是由于歷史原因,不同瀏覽器實(shí)現(xiàn)不同,所以一般配合這三個(gè)頭使用
 6.1控制瀏覽器不要緩存(驗(yàn)證碼圖片不緩存)設(shè)置expires為0或-1設(shè)置Cache-Control為no-cache、Pragma為no-cache
 6.2控制瀏覽器緩存資源。即使不明確指定瀏覽器也會(huì)緩存資源,這種緩存沒(méi)有截至日期。當(dāng)在地址欄重新輸入地址時(shí)會(huì)用緩存,但是當(dāng)刷新或重新開(kāi)瀏覽器訪(fǎng)問(wèn)時(shí)會(huì)重新獲得資源。
 如果明確指定緩存時(shí)間,瀏覽器緩存是,會(huì)有一個(gè)截至日期,在截至日期到期之前,當(dāng)在地址欄重新輸入地址或重新開(kāi)瀏覽器訪(fǎng)問(wèn)時(shí)都會(huì)用緩存,而當(dāng)刷新時(shí)會(huì)重新獲得資源。
 7.Response實(shí)現(xiàn)請(qǐng)求重定向
 7.1古老方法:response.setStatus(302);response.addHeader('Location','URL');
 7.2快捷方式:response.sendRedirect('URL');
 
 8.Response注意的內(nèi)容:
 8.1對(duì)于一次請(qǐng)求,Response的getOutputStream方法和getWriter方法是互斥,只能調(diào)用其一,特別注意forward后也不要違反這一規(guī)則。
 8.2利用Response輸出數(shù)據(jù)的時(shí)候,并不是直接將數(shù)據(jù)寫(xiě)給瀏覽器,而是寫(xiě)到了Response的緩沖區(qū)中,等到整個(gè)service方法返回后,由服務(wù)器拿出response中的信息組成響應(yīng)消息返回給瀏覽器。
 8.3service方法返回后,服務(wù)器會(huì)自己檢查Response獲取的OutputStream或者Writer是否關(guān)閉,如果沒(méi)有關(guān)閉,服務(wù)器自動(dòng)幫你關(guān)閉,一般情況下不要自己關(guān)閉這兩個(gè)流。
 
 
 二、Request:Request代表請(qǐng)求對(duì)象,其中封裝了對(duì)請(qǐng)求中具有請(qǐng)求行、請(qǐng)求頭、實(shí)體內(nèi)容的操作的方法
 1.獲取客戶(hù)機(jī)信息
 getRequestURL方法返回客戶(hù)端發(fā)出請(qǐng)求完整URL    eg:返回: ' http://localhost:8080/Day05/index.jsp'
 getRequestURI方法返回請(qǐng)求行中的資源名部分,在權(quán)限控制中常用 eg:返回  '/Day05/index.jsp'
 getQueryString 方法返回請(qǐng)求行中的參數(shù)部分
 getRemoteAddr方法返回發(fā)出請(qǐng)求的客戶(hù)機(jī)的IP地址
 getMethod得到客戶(hù)機(jī)請(qǐng)求方式
 getContextPath 獲得當(dāng)前web應(yīng)用虛擬目錄名稱(chēng),特別重要?。?!,工程中所有的路徑請(qǐng)不要硬編碼(不要寫(xiě)死),其中的web應(yīng)用名要以此方法去獲得。
 
 2.獲取請(qǐng)求頭信息
 getHeader(name)方法 --- String ,獲取指定名稱(chēng)的請(qǐng)求頭的值
 getHeaders(String name)方法 --- Enumeration ,獲取指定名稱(chēng)的請(qǐng)求頭的值的集合,因?yàn)榭赡艹霈F(xiàn)多個(gè)重名的請(qǐng)求頭
 getHeaderNames方法 --- Enumeration ,獲取所有請(qǐng)求頭名稱(chēng)組成的集合
 getIntHeader(name)方法  --- int ,獲取int類(lèi)型的請(qǐng)求頭的值
 getDateHeader(name)方法 --- long(日期對(duì)應(yīng)毫秒) ,獲取一個(gè)日期型的請(qǐng)求頭的值,返回的是一個(gè)long值,從1970年1月1日0時(shí)開(kāi)始的毫秒值
 
 *實(shí)驗(yàn):通過(guò)referer信息防盜鏈(防盜鏈:非正常訪(fǎng)問(wèn),簡(jiǎn)單的是,就是不是從規(guī)定的站點(diǎn)發(fā)送HTTP請(qǐng)求,而是從其他站點(diǎn)發(fā)送的HTTP請(qǐng)求)
 String ref = request.getHeader('Referer');
 if (ref == null || ref == '' || !ref.startsWith('http://localhost')) {
 response.sendRedirect(request.getContextPath() + '/homePage.html');
 } else {
 //為了不讓其他資源可見(jiàn),fengjie.html應(yīng)該存放在WEB-INF目錄下
 this.getServletContext().getRequestDispatcher('/WEB-INF/fengjie.html').forward(request, response);
 }
 3.獲取請(qǐng)求參數(shù)
 getParameter(name) --- String 通過(guò)name獲得值
 getParameterValues(name)  --- String[ ] 通過(guò)name獲得多值 checkbox
 getParameterNames  --- Enumeration 獲得所有請(qǐng)求參數(shù)名稱(chēng)組成的枚舉
 getParameterMap  --- Map 獲取所有請(qǐng)求參數(shù)的組成的Map集合,注意,其中的鍵為String,值為String[]
 
 獲取請(qǐng)求參數(shù)時(shí)亂碼問(wèn)題:
 瀏覽器發(fā)送的請(qǐng)求參數(shù)使用什么編碼呢?當(dāng)初瀏覽器打開(kāi)網(wǎng)頁(yè)時(shí)使用什么編碼,發(fā)送就用什么編碼。
 服務(wù)器端獲取到發(fā)過(guò)來(lái)的請(qǐng)求參數(shù)默認(rèn)使用ISO8859-1進(jìn)行解碼操作,中文一定有亂碼問(wèn)題
 對(duì)于Post方式提交的數(shù)據(jù),可以設(shè)置request.setCharacterEncoding('gb2312');來(lái)明確指定獲取請(qǐng)求參數(shù)時(shí)使用編碼。但是此種方式只對(duì)Post方式提交有效。
 對(duì)于Get方式提交的數(shù)據(jù),就只能手動(dòng)解決亂碼:String newName = new String(name.getBytes('ISO8859-1'),'gb2312');此種方法對(duì)Post方式同樣有效。
 request.setCharacterEncoding('gb2312');對(duì)GET方式提交無(wú)效的原因是:該代碼只設(shè)置請(qǐng)求實(shí)體的編碼,而GET提交的數(shù)據(jù)是存放在請(qǐng)求行中的[資源名?param1='張三'¶m2=123],所以對(duì)GET請(qǐng)求的方式無(wú)效。
 在tomcat的server.xml中可以配置http連接器的URIEncoding可以指定服務(wù)器在獲取請(qǐng)求參數(shù)時(shí)默認(rèn)使用的編碼,從而一勞永逸的決絕獲取請(qǐng)求參數(shù)時(shí)的亂碼問(wèn)題。也可以指定useBodyEncodingForURI參數(shù),令request.setCharacterEncoding也對(duì)GET方式的請(qǐng)求起作用,但是這倆屬性都不推薦使用,因?yàn)榘l(fā)布環(huán)境往往不允許修改此屬性。
 
 
 4.利用請(qǐng)求域傳遞對(duì)象
 生命周期:在service方法調(diào)用之前由服務(wù)器創(chuàng)建,傳入service方法。整個(gè)請(qǐng)求結(jié)束,request生命結(jié)束。
 作用范圍:整個(gè)請(qǐng)求鏈。
 作用:在整個(gè)請(qǐng)求鏈中共享數(shù)據(jù),最常用的:在Servlet中處理好的數(shù)據(jù)要交給Jsp顯示,此時(shí)參數(shù)就可以放置在Request域中帶過(guò)去。
 
 5.request實(shí)現(xiàn)請(qǐng)求轉(zhuǎn)發(fā)
 ServletContext可以實(shí)現(xiàn)請(qǐng)求轉(zhuǎn)發(fā),request也可以。
 在forward之前輸入到response緩沖區(qū)中的數(shù)據(jù),如果已經(jīng)被發(fā)送到了客戶(hù)端,forward將失敗,拋出異常
 在forward之前輸入到response緩沖區(qū)中的數(shù)據(jù),但是還沒(méi)有發(fā)送到客戶(hù)端,forward可以執(zhí)行,但是緩沖區(qū)將被清空,之前的數(shù)據(jù)丟失。注意丟失的只是請(qǐng)求體中的內(nèi)容,頭內(nèi)容仍然有效。
 在一個(gè)Servlet中進(jìn)行多次forward也是不行的,因?yàn)榈谝淮蝔orward結(jié)束,response已經(jīng)被提交了,沒(méi)有機(jī)會(huì)再forward了
 總之,一條原則,一次請(qǐng)求只能有一次響應(yīng),響應(yīng)提交走后,就再?zèng)]有機(jī)會(huì)輸出數(shù)據(jù)給瀏覽器了。
 
 6.RequestDispatcher進(jìn)行include操作
 forward沒(méi)有辦法將多個(gè)servlet的輸出組成一個(gè)輸出,因此RequestDispatcher提供了include方法,可以將多個(gè)Servlet的輸出組成一個(gè)輸出返回個(gè)瀏覽器
 request.getRequestDispatcher('/servlet/Demo17Servlet').include(request, response);
 response.getWriter().write('from Demo16');
 request.getRequestDispatcher('/servlet/Demo18Servlet').include(request, response);
 常用在頁(yè)面的固定部分單獨(dú)寫(xiě)入一個(gè)文件,在多個(gè)頁(yè)面中include進(jìn)來(lái)簡(jiǎn)化代碼量。
 
 
 
 三、地址的寫(xiě)法:
 
 絕對(duì)路徑(以斜線(xiàn)開(kāi)頭的路徑,代表相對(duì)與當(dāng)前web應(yīng)用):
 如果地址是給服務(wù)器用的,web應(yīng)用的名稱(chēng)可以省略。如果地址是給客戶(hù)端用的,必須寫(xiě)上web應(yīng)用名
 request.getRequestDispatcher('/index.jsp').include(request, response);
 response.setHeader('Location','/Day05/index.jsp');
 response.sendRedirect('/Day05/index.jsp');
 this.getServletContext().getRealPath('/index.jsp');
 this.getServletContext().getResourceAsStream('/index.jsp');
 
 
 
 類(lèi)加載器加載資源的時(shí)候,相對(duì)于WEB-INF下的classes目錄
 this.getClass().getClassLoader().getResource('');
 this.getClass().getClassLoader().getResourceAsStream('');
 相對(duì)路徑(不以斜杠開(kāi)頭的路徑,要參考當(dāng)前所在的路徑去拼新的路徑)---除了在必須使用的情況外,都不要使用相對(duì)路徑:
 如果直接寫(xiě)相對(duì)路徑或?qū)?/相對(duì)路徑的話(huà),相對(duì)路徑替換當(dāng)前路徑最后一級(jí)
 如果寫(xiě)../相對(duì)路徑,則替換當(dāng)前路徑的最后一級(jí)路徑的上一級(jí)路徑。
 如果想替換更高層,則寫(xiě)多個(gè)../
 
 四、URL編碼
 1.由于HTTP協(xié)議規(guī)定URL路徑中只能存在ASCII碼中的字符,所以如果URL中存在中文或特殊字符需要進(jìn)行URL編碼。
 2.編碼原理:
 將空格轉(zhuǎn)換為加號(hào)(+)
 對(duì)0-9,a-z,A-Z之間的字符保持不變
 對(duì)于所有其他的字符,用這個(gè)字符的當(dāng)前字符集編碼在內(nèi)存中的十六進(jìn)制格式表示,并在每個(gè)字節(jié)前加上一個(gè)百分號(hào)(%)。如字符“+”用%2B表示,字符“=”用%3D表示,字符“&”用%26表示,每個(gè)中文字符在內(nèi)存中占兩個(gè)字節(jié),字符“中”用%D6%D0表示,字符“國(guó)”用%B9%FA表示,對(duì)于空格也可以直接使用其十六進(jìn)制編碼方式,即用%20表示,而不是將它轉(zhuǎn)換成加號(hào)(+)
 說(shuō)明:
 如果確信URL串的特殊字符沒(méi)有引起使用上的岐義或沖突你也可以對(duì)這些字符不進(jìn)行編碼,而是直接傳遞給服務(wù)器。例如,http://www./dealregister.html?name=中國(guó)&password=123
 如果URL串中的特殊字符可能會(huì)產(chǎn)生岐義或沖突,則必須對(duì)這些特殊字符進(jìn)行URL編碼。例如,服務(wù)器會(huì)將不編碼的“中+國(guó)”當(dāng)作“中國(guó)”處理。還例如,當(dāng)name參數(shù)值為“中&國(guó)”時(shí),如果不對(duì)其中的“&”編碼,URL字符串將有如下形式:http://www./dealregister.html?name=中&國(guó)&password=123,應(yīng)編碼為:http://www./dealregister.html?name=中%26國(guó)&password=123
 http://www./example/index.html#section2可改寫(xiě)成http://www./example%2Findex.html%23section2
 3.在java中進(jìn)行URL編碼和解碼
 URLEncoder.encode('xxxx','utf-8');
 URLDecoder.decode(str,'utf-8');
 
 
 五、請(qǐng)求重定向和請(qǐng)求轉(zhuǎn)發(fā)的區(qū)別
 1.區(qū)別
 RequestDispatcher.forward方法只能將請(qǐng)求轉(zhuǎn)發(fā)給同一個(gè)WEB應(yīng)用中的組件;而HttpServletResponse.sendRedirect 方法還可以重定向到同一個(gè)站點(diǎn)上的其他應(yīng)用程序中的資源,甚至是使用絕對(duì)URL重定向到其他站點(diǎn)的資源。
 如果傳遞給HttpServletResponse.sendRedirect 方法的相對(duì)URL以“/”開(kāi)頭,它是相對(duì)于服務(wù)器的根目錄;如果創(chuàng)建RequestDispatcher對(duì)象時(shí)指定的相對(duì)URL以“/”開(kāi)頭,它是相對(duì)于當(dāng)前WEB應(yīng)用程序的根目錄。
 調(diào)用HttpServletResponse.sendRedirect方法重定向的訪(fǎng)問(wèn)過(guò)程結(jié)束后,瀏覽器地址欄中顯示的URL會(huì)發(fā)生改變,由初始的URL地址變成重定向的目標(biāo)URL;調(diào)用RequestDispatcher.forward 方法的請(qǐng)求轉(zhuǎn)發(fā)過(guò)程結(jié)束后,瀏覽器地址欄保持初始的URL地址不變。
 HttpServletResponse.sendRedirect方法對(duì)瀏覽器的請(qǐng)求直接作出響應(yīng),響應(yīng)的結(jié)果就是告訴瀏覽器去重新發(fā)出對(duì)另外一個(gè)URL的訪(fǎng)問(wèn)請(qǐng)求;RequestDispatcher.forward方法在服務(wù)器端內(nèi)部將請(qǐng)求轉(zhuǎn)發(fā)給另外一個(gè)資源,瀏覽器只知道發(fā)出了請(qǐng)求并得到了響應(yīng)結(jié)果,并不知道在服務(wù)器程序內(nèi)部發(fā)生了轉(zhuǎn)發(fā)行為。
 RequestDispatcher.forward方法的調(diào)用者與被調(diào)用者之間共享相同的request對(duì)象和response對(duì)象,它們屬于同一個(gè)訪(fǎng)問(wèn)請(qǐng)求和響應(yīng)過(guò)程;而HttpServletResponse.sendRedirect方法調(diào)用者與被調(diào)用者使用各自的request對(duì)象和response對(duì)象,它們屬于兩個(gè)獨(dú)立的訪(fǎng)問(wèn)請(qǐng)求和響應(yīng)過(guò)程。
 2.應(yīng)用場(chǎng)景(參照?qǐng)D想)
 通常情況下都用請(qǐng)求轉(zhuǎn)發(fā),減少服務(wù)器壓力
 當(dāng)需要更新地址欄時(shí)用請(qǐng)求重定向,如注冊(cè)成功后跳轉(zhuǎn)到主頁(yè)。
 當(dāng)需要刷新更新操作時(shí)用請(qǐng)求重定向,如購(gòu)物車(chē)付款的操作。
 
 
 |