1. 程式人生 > >java Request物件和Response物件詳解

java Request物件和Response物件詳解

一:概述
Web伺服器收到客戶端的http請求,會針對每一次請求,分別建立一個用於代表請求的request物件、和代表響應的response物件

一、Response物件
    1.Resonse的繼承結構:
            ServletResponse--HttpServletResponse
    2.Response代表響應,於是響應訊息中的 狀態碼、響應頭、實體內容都可以由它進行操作,由此引伸出如下實驗:
    3.利用Response輸出資料到客戶端
       response.getOutputStream().write("中文".getBytes())輸出資料,這是一個位元組流,寫入記憶體使用什麼編碼,輸出就使用什麼編碼,而瀏覽器預設用平臺位元組碼開啟伺服器傳送的資料,如果伺服器端使用了非平臺碼去輸出字元的位元組資料就需要明確的指定瀏覽器編碼時所用的碼錶,以防止亂碼問題。
       response.addHeader("Content-type","text/html;charset=gb2312")
       response.getWriter().write(“中文”);輸出資料,這是一個字元流,response會將此字元進行轉碼操作後輸出到瀏覽器,這個過程預設使用ISO8859-1碼錶,而ISO8859-1中沒有中文,於是轉碼過程中用?代替了中文,導致亂碼問題。可以指定response在轉碼過程中使用的目標碼錶,防止亂碼。response.setCharacterEncoding("gb2312");
       其實response還提供了setContentType("text/html;charset=gb2312")方法,此方法會設定content-type響應頭,通知瀏覽器開啟的碼錶,同時設定response的轉碼時使的用碼錶,從而一行程式碼解決亂碼。
    4.利用Response 設定 content-disposition頭實現檔案下載
		設定響應頭content-disposition為“attachment;filename=xxx.xxx”
		利用流將檔案讀取進來,再利用Response獲取響應流輸出
		如果檔名為中,一定要進行URL編碼,編碼所用的碼錶一定要是UTF-8
    5.refresh頭控制定時重新整理
        設定響應頭Refresh為一個數值,指定多少秒後重新整理當前頁面
        設定響應頭Refresh為 3;url=/Day05/index.jsp,指定多少秒後重新整理到哪個頁面
        可以用來實現註冊後“註冊成功,3秒後跳轉到主頁”的功能
        在HTML可以利用<meta http-equiv= "" content="">標籤模擬響應頭的功能。
      eg:<meta http-equiv= "Refresh" content="3;url=/Day05/index.jsp">
    6.利用response設定expires、Cache-Control、Pragma實現瀏覽器是否快取資源,這三個頭都可以實現,但是由於歷史原因,不同瀏覽器實現不同,所以一般配合這三個頭使用
        6.1控制瀏覽器不要快取(驗證碼圖片不快取)設定expires為0或-1設定Cache-Control為no-cache、Pragma為no-cache
        6.2控制瀏覽器快取資源。即使不明確指定瀏覽器也會快取資源,這種快取沒有截至日期。當在位址列重新輸入地址時會用快取,但是當重新整理或重新開瀏覽器訪問時會重新獲得資源。
            如果明確指定快取時間,瀏覽器快取是,會有一個截至日期,在截至日期到期之前,當在位址列重新輸入地址或重新開瀏覽器訪問時都會用快取,而當重新整理時會重新獲得資源。
    7.Response實現請求重定向
        7.1古老方法:response.setStatus(302);response.addHeader("Location","URL");
        7.2快捷方式:response.sendRedirect("URL");
   
    8.Response注意的內容:
        8.1對於一次請求,Response的getOutputStream方法和getWriter方法是互斥,只能呼叫其一,特別注意forward後也不要違反這一規則。
        8.2利用Response輸出資料的時候,並不是直接將資料寫給瀏覽器,而是寫到了Response的緩衝區中,等到整個service方法返回後,由伺服器拿出response中的資訊組成響應訊息返回給瀏覽器。
        8.3service方法返回後,伺服器會自己檢查Response獲取的OutputStream或者Writer是否關閉,如果沒有關閉,伺服器自動幫你關閉,一般情況下不要自己關閉這兩個流。
        

二、Request:Request代表請求物件,其中封裝了對請求中具有請求行、請求頭、實體內容的操作的方法
    1.獲取客戶機資訊
        getRequestURL方法返回客戶端發出請求完整URL    eg:返回: " http://localhost:8080/Day05/index.jsp"
        getRequestURI方法返回請求行中的資源名部分,在許可權控制中常用 eg:返回  "/Day05/index.jsp"
        getQueryString 方法返回請求行中的引數部分
        getRemoteAddr方法返回發出請求的客戶機的IP地址
        getMethod得到客戶機請求方式
        getContextPath 獲得當前web應用虛擬目錄名稱,特別重要!!!,工程中所有的路徑請不要硬編碼(不要寫死),其中的web應用名要以此方法去獲得。

    2.獲取請求頭資訊
        getHeader(name)方法 --- String ,獲取指定名稱的請求頭的值
        getHeaders(String name)方法 --- Enumeration<String> ,獲取指定名稱的請求頭的值的集合,因為可能出現多個重名的請求頭
        getHeaderNames方法 --- Enumeration<String> ,獲取所有請求頭名稱組成的集合
        getIntHeader(name)方法  --- int ,獲取int型別的請求頭的值
        getDateHeader(name)方法 --- long(日期對應毫秒) ,獲取一個日期型的請求頭的值,返回的是一個long值,從1970年1月1日0時開始的毫秒值
        
        *實驗:通過referer資訊防盜鏈(防盜鏈:非正常訪問,簡單的是,就是不是從規定的站點發送HTTP請求,而是從其他站點發送的HTTP請求)
		String ref = request.getHeader("Referer");
		if (ref == null || ref == "" || !ref.startsWith("http://localhost")) {
			response.sendRedirect(request.getContextPath() + "/homePage.html");
		} else {
			//為了不讓其他資源可見,fengjie.html應該存放在WEB-INF目錄下
			this.getServletContext().getRequestDispatcher("/WEB-INF/fengjie.html").forward(request, response);
		}
    3.獲取請求引數
        getParameter(name) --- String 通過name獲得值
        getParameterValues(name)  --- String[ ] 通過name獲得多值 checkbox
        getParameterNames  --- Enumeration<String> 獲得所有請求引數名稱組成的列舉
        getParameterMap  --- Map<String,String[ ]> 獲取所有請求引數的組成的Map集合,注意,其中的鍵為String,值為String[]
        
        獲取請求引數時亂碼問題:
            瀏覽器傳送的請求引數使用什麼編碼呢?當初瀏覽器開啟網頁時使用什麼編碼,傳送就用什麼編碼。
            伺服器端獲取到發過來的請求引數預設使用ISO8859-1進行解碼操作,中文一定有亂碼問題
            對於Post方式提交的資料,可以設定request.setCharacterEncoding("gb2312");來明確指定獲取請求引數時使用編碼。但是此種方式只對Post方式提交有效。
            對於Get方式提交的資料,就只能手動解決亂碼:String newName = new String(name.getBytes("ISO8859-1"),"gb2312");此種方法對Post方式同樣有效。
            request.setCharacterEncoding("gb2312");對GET方式提交無效的原因是:該程式碼只設置請求實體的編碼,而GET提交的資料是存放在請求行中的[資源名?param1="張三"¶m2=123],所以對GET請求的方式無效。
            在tomcat的server.xml中可以配置http聯結器的URIEncoding可以指定伺服器在獲取請求引數時預設使用的編碼,從而一勞永逸的決絕獲取請求引數時的亂碼問題。也可以指定useBodyEncodingForURI引數,令request.setCharacterEncoding也對GET方式的請求起作用,但是這倆屬性都不推薦使用,因為釋出環境往往不允許修改此屬性。
            
            
    4.利用請求域傳遞物件
        生命週期:在service方法呼叫之前由伺服器建立,傳入service方法。整個請求結束,request生命結束。
        作用範圍:整個請求鏈。
        作用:在整個請求鏈中共享資料,最常用的:在Servlet中處理好的資料要交給Jsp顯示,此時引數就可以放置在Request域中帶過去。
        
    5.request實現請求轉發
        ServletContext可以實現請求轉發,request也可以。
        在forward之前輸入到response緩衝區中的資料,如果已經被髮送到了客戶端,forward將失敗,丟擲異常
        在forward之前輸入到response緩衝區中的資料,但是還沒有傳送到客戶端,forward可以執行,但是緩衝區將被清空,之前的資料丟失。注意丟失的只是請求體中的內容,頭內容仍然有效。
        在一個Servlet中進行多次forward也是不行的,因為第一次forward結束,response已經被提交了,沒有機會再forward了
        總之,一條原則,一次請求只能有一次響應,響應提交走後,就再沒有機會輸出資料給瀏覽器了。
        
    6.RequestDispatcher進行include操作
        forward沒有辦法將多個servlet的輸出組成一個輸出,因此RequestDispatcher提供了include方法,可以將多個Servlet的輸出組成一個輸出返回個瀏覽器
		request.getRequestDispatcher("/servlet/Demo17Servlet").include(request, response);
		response.getWriter().write("from Demo16");
		request.getRequestDispatcher("/servlet/Demo18Servlet").include(request, response);
        常用在頁面的固定部分單獨寫入一個檔案,在多個頁面中include進來簡化程式碼量。
        
        

三、地址的寫法:

絕對路徑(以斜線開頭的路徑,代表相對與當前web應用):
    如果地址是給伺服器用的,web應用的名稱可以省略。如果地址是給客戶端用的,必須寫上web應用名
    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");
    <a href="/Day05/index.jsp">
    <from action="/Day05/index.jsp">
    
    類載入器載入資源的時候,相對於WEB-INF下的classes目錄
    this.getClass().getClassLoader().getResource("");
    this.getClass().getClassLoader().getResourceAsStream("");
相對路徑(不以斜槓開頭的路徑,要參考當前所在的路徑去拼新的路徑)---除了在必須使用的情況外,都不要使用相對路徑:
    如果直接寫相對路徑或寫./相對路徑的話,相對路徑替換當前路徑最後一級
    如果寫../相對路徑,則替換當前路徑的最後一級路徑的上一級路徑。
    如果想替換更高層,則寫多個../
    
四、URL編碼
    1.由於HTTP協議規定URL路徑中只能存在ASCII碼中的字元,所以如果URL中存在中文或特殊字元需要進行URL編碼。
    2.編碼原理:
        將空格轉換為加號(+) 
        對0-9,a-z,A-Z之間的字元保持不變 
        對於所有其他的字元,用這個字元的當前字符集編碼在記憶體中的十六進位制格式表示,並在每個位元組前加上一個百分號(%)。如字元“+”用%2B表示,字元“=”用%3D表示,字元“&”用%26表示,每個中文字元在記憶體中佔兩個位元組,字元“中”用%D6%D0表示,字元“國”用%B9%FA表示,對於空格也可以直接使用其十六進位制編碼方式,即用%20表示,而不是將它轉換成加號(+) 
        說明:
        如果確信URL串的特殊字元沒有引起使用上的岐義或衝突你也可以對這些字元不進行編碼,而是直接傳遞給伺服器。例如,http://www.it315.org/dealregister.html?name=中國&password=123 
        如果URL串中的特殊字元可能會產生岐義或衝突,則必須對這些特殊字元進行URL編碼。例如,伺服器會將不編碼的“中+國”當作“中國”處理。還例如,當name引數值為“中&國”時,如果不對其中的“&”編碼,URL字串將有如下形式:http://www.it315.org/dealregister.html?name=中&國&password=123,應編碼為:http://www.it315.org/dealregister.html?name=中%26國&password=123 
        http://www.it315.org/example/index.html#section2可改寫成http://www.it315.org/example%2Findex.html%23section2 
    3.在java中進行URL編碼和解碼
        URLEncoder.encode("xxxx","utf-8");
        URLDecoder.decode(str,"utf-8");


五、請求重定向和請求轉發的區別 
    1.區別
		RequestDispatcher.forward方法只能將請求轉發給同一個WEB應用中的元件;而HttpServletResponse.sendRedirect 方法還可以重定向到同一個站點上的其他應用程式中的資源,甚至是使用絕對URL重定向到其他站點的資源。 
		如果傳遞給HttpServletResponse.sendRedirect 方法的相對URL以“/”開頭,它是相對於伺服器的根目錄;如果建立RequestDispatcher物件時指定的相對URL以“/”開頭,它是相對於當前WEB應用程式的根目錄。 
		呼叫HttpServletResponse.sendRedirect方法重定向的訪問過程結束後,瀏覽器位址列中顯示的URL會發生改變,由初始的URL地址變成重定向的目標URL;呼叫RequestDispatcher.forward 方法的請求轉發過程結束後,瀏覽器位址列保持初始的URL地址不變。
		HttpServletResponse.sendRedirect方法對瀏覽器的請求直接作出響應,響應的結果就是告訴瀏覽器去重新發出對另外一個URL的訪問請求;RequestDispatcher.forward方法在伺服器端內部將請求轉發給另外一個資源,瀏覽器只知道發出了請求並得到了響應結果,並不知道在伺服器程式內部發生了轉發行為。 
		RequestDispatcher.forward方法的呼叫者與被呼叫者之間共享相同的request物件和response物件,它們屬於同一個訪問請求和響應過程;而HttpServletResponse.sendRedirect方法呼叫者與被呼叫者使用各自的request物件和response物件,它們屬於兩個獨立的訪問請求和響應過程。 
    2.應用場景(參照圖想)
        通常情況下都用請求轉發,減少伺服器壓力
        當需要更新位址列時用請求重定向,如註冊成功後跳轉到主頁。
        當需要重新整理更新操作時用請求重定向,如購物車付款的操作。