JavaWeb之Request && Response篇

Reuqest 和 Response
服務器處理請求的流程
服務器每次收到請求時,都會爲這個請求開闢一個新的線程。
服務器會把客戶端的請求數據封裝到request對象中,request就是請求數據的載體!(request中存放着本次請求的所有數據)
服務器還會創建response對象,這個對象與客戶端連接在一起,它可以用來向客戶端發送響應。(response中存放着服務器針對本次請求處理的所有數據)
一、request

request是Servlet.service()方法的一個參數,類型爲javax.servlet.http.HttpServletRequest。在客戶端發出每個請求時,服務器都會創建一個request對象,並把請求數據封裝到request中,然後在調用Servlet.service()方法時傳遞給service()方法,這說明在service()方法中可以通過request對象來獲取請求數據.再使用response完成響應

response對象的功能:

1.設置響應頭信息

2.發送狀態碼

3.設置響應正文

4.重定向

1.response響應正文:

repsonse一共提供了兩個響應流對象:

PrintWriter p = response.getWriter();獲取字符流

ServletOutputStream out = response.getOutputStream();獲取字節流

針對需求使用特定的流,但是,這兩個流不能同時使用,否則會拋出illegalStateException異常

字符編碼

在使用response.getWriter()時需要注意默認字符編碼爲ISO-8859-1,如果希望設置字符流的字符編碼爲utf-8,可以使用response.setCharaceterEncoding(“utf-8”)來設置。這樣可以保證輸出給客戶端的字符都是使用UTF-8編碼的!

但客戶端瀏覽器並不知道響應數據是什麼編碼的!如果希望通知客戶端使用UTF-8來解讀響應數據,那麼還是使用response.setContentType("text/html;charset=utf-8")方法比較好,因爲這個方法不只會調用response.setCharaceterEncoding(“utf-8”),還會設置content-type響應頭,客戶端瀏覽器會使用content-type頭來解讀響應數據。

response.getWriter()是PrintWriter類型,所以它有緩衝區,緩衝區的默認大小爲8KB,在沒有超過8kb時,是不會發送給客戶端的,可以使用response.flushBuffer()方法來手動刷新緩衝區

2.設置響應頭信息

使用response對象的setHeader()方法來設置響應頭!使用該方法設置的響應頭最終會發送給客戶端瀏覽器!

response.setHeader(“content-type”, “text/html;charset=utf-8”):設置content-type響應頭,該頭的作用是告訴瀏覽器響應內容爲html類型,編碼爲utf-8。而且同時會設置response的字符流編碼爲utf-8,即response.setCharaceterEncoding(“utf-8”);

自動刷新,response.setHeader("Refresh","5; URL=http://www.baidu .cn"):5秒後自動跳轉到百度主頁

3.設置狀態碼及其他方法

response.setContentType("text/html;charset=utf-8"):等同與調用response.setHeader(“content-type”, “text/html;charset=utf-8”),一般使用setContenType來設置

response.setCharacterEncoding(“utf-8”):設置字符響應流的字符編碼爲utf-8; 

response.setStatus(200):設置狀態碼;

response.sendError(404, “您要查找的資源不存在”):當發送錯誤狀態碼時,Tomcat會跳轉到固定的錯誤頁面去,但可以顯示錯誤信息。

4.重定向

當訪問一個地址時,瀏覽器自動跳轉到另一個地址時,這就是重定向

也就是客戶端發出請求到服務器,服務器做出響應,並通知客戶端它的請求處理不了,請去另一個指定地方,客戶端又發出一次請求到指定地方.

響應碼爲200標識響應成功,302表示重定向,因爲重定向是通知瀏覽器進行第二次請求,所以除了響應碼還需要進行設置第二次請求的URL

response.setStatus(302);
response.setHeader("Location", "http://www.baidu.cn");
更爲便捷的重定向的方式是

response.sendRedirect("http://www.baidu.cn");
自動設置狀態碼

重定向總結

重定向是兩次請求;

重定向的URL可以是其他應用,不侷限於當前應用;

重定向的響應頭爲302,並且必須要有Location響應頭;

重定向就不要再使用response.getWriter()response.getOutputStream()輸出數據,不然可能會出現異常;

二、Request

request是Servlet.service()方法的一個參數,類型爲javax.servlet.http.HttpServletRequest。在客戶端發出每個請求時,服務器都會創建一個request對象,並把請求數據封裝到request中,然後在調用Servlet.service()方法時傳遞給service()方法,這說明在service()方法中可以通過request對象來獲取請求數據。

request的功能:

封裝了請求頭數據;

封裝了請求正文數據,如果是GET請求,那麼就沒有正文;

request是一個域對象,可以把它當成Map來添加獲取數據;

request提供了請求轉發和請求包含功能。

request的域方法

一個請求會創建一個request對象,如果在一個請求中經歷了多個Servlet,那麼多個Servlet就可以使用request來共享數據。

void setAttribute(String name, Object value):用來存儲一個對象,也可以稱之爲存儲一個域屬性,例如:servletContext.setAttribute(“xxx”, “XXX”),在request中保存了一個域屬性,域屬性名稱爲xxx,域屬性的值爲XXX。請注意,如果多次調用該方法,並且使用相同的name,那麼會覆蓋上一次的值,這一特性與Map相同;

Object getAttribute(String name):用來獲取request中的數據,當前在獲取之前需要先去存儲纔行,例如:String value = (String)request.getAttribute(“xxx”);,獲取名爲xxx的域屬性,注意強轉;

void removeAttribute(String name):用來移除request中的域屬性,如果參數name指定的域屬性不存在,那麼本方法什麼都不做;

Enumeration getAttributeNames():獲取所有域屬性的名稱;

request獲取請求頭數據

String getHeader(String name):獲取指定名稱的請求頭;

Enumeration getHeaderNames():獲取所有請求頭名稱;

int getIntHeader(String name):獲取值爲int類型的請求頭

其他方法

l int getContentLength():獲取請求體的字節數,GET請求沒有請求體,沒有請求體返回-1

l String getContentType():獲取請求類型,baidusysop如果請求是GET,那麼這個方法返回null;如果是POST請求,那麼默認爲application/x-www-form-urlencoded,表示請求體內容使用了URL編碼;

l String getMethod():返回請求方法,例如:GET

l Locale getLocale():返回當前客戶端瀏覽器的Localejava.util.Locale表示國家和言語,這個東西在國際化中很有用;

l String getCharacterEncoding():獲取請求編碼,如果沒有setCharacterEncoding(),那麼返回null,表示使用ISO-8859-1編碼;

l void setCharacterEncoding(String code):設置請求編碼,只對請求體有效!注意,對於GET而言,沒有請求體!!!所以此方法只能對POST請求中的參數有效!

l String getContextPath():返回上下文路徑,例如:/hello

l String getQueryString():返回請求URL中的參數,例如:name=zhangSan

l String getRequestURI():返回請求URI路徑,例如:/hello/oneServlet

l StringBuffer getRequestURL():返回請求URL路徑,例如:http://localhost/hello/oneServlet,即返回除了參數以外的路徑信息;

l String getServletPath():返回Servlet路徑,例如:/oneServlet

l String getRemoteAddr():返回當前客戶端的IP地址;

l String getRemoteHost():返回當前客戶端的主機名,但這個方法的實現還是獲取IP地址;

l String getScheme():返回請求協議,例如:http

l String getServerName():返回主機名,例如:localhost

int getServerPort():返回服務器端口號,例如:8080


public void doGet(){
	String ip = request.getRemoteAddr();
	String userAgent = request.getHeader("User-Agent");
	if(userAgent.toLowerCase().contains("firefox")){
				syso("火狐 IP"+ip);
	}else if(userAgent.toLowerCase().contains("chrome")){

	}else if(userAgent.toLowerCase().contains("msie")){
			syso("IE IP"+ip);
	}
}

使用Referer請求頭,設置防盜鏈

public void doGet(){
	String referer = request.getHeader("Referer");
	if(referer == null || !referer.contains("localhost")){  // 如果請求不是來自我本站,那麼重定向到百度
		response.sendRedirect("http://www.baidu.com");
	}
}

request請求參數

瀏覽器地址欄直接輸入:一定是GET請求;

超鏈接:一定是GET請求;

表單:可以是GET,也可以是POST,這取決與<form>的method屬性值;

 

GET請求和POST請求的區別:

GET請求:

請求參數會在瀏覽器的地址欄中顯示

請求參數長度限制長度在1K之內;

GET請求沒有請求體,無法通過request.setCharacterEncoding()來設置參數的編碼;

POST請求:

請求參數不會顯示瀏覽器的地址欄,相對安全;

請求參數長度沒有限制;

l String getParameter(String name):通過指定名稱獲取參數值;

String[] getParameterValues(String name):當多個參數名稱相同時,可以使用方法來獲取

l Enumeration getParameterNames():獲取所有參數的名字;

Map getParameterMap():獲取所有參數封裝到Map中,其中key爲參數名,value爲參數值,因爲一個參數名稱可能有多個值,所以參數值是String[],而不是String

請求轉發和請求包含

請求轉發(forwars常用)和包含(include),是在多個Servlet中共享數據,是同一個request,處於同一份請求範圍內。

對於GET請求:


請求轉發

RequestDispatcher rd = request.getRequestDispatcher("/BServlet");[獲取“調度器”,其中參數爲BServlet綁定的URL,即BServlet的<url-pattern>值。]
		rd.forward(request, response)[調用“調度器”的轉發方法,該方法等同與告訴服務器,去調用BServlet的service()方法一樣。];
請求包含
RequestDispatcher rd = request.getRequestDispatcher("/BServlet");
		rd.include[請求包含](request, response);

請求轉發與請求包含比較

l 如果在AServlet中請求轉發到BServlet,那麼在AServlet中就不允許再輸出響應體,即不能再使用response.getWriter()response.getOutputStream()向客戶端輸出,這一工作應該由BServlet來完成;如果是使用請求包含,那麼沒有這個限制;

l 請求轉發雖然不能輸出響應體,但還是可以設置響應頭的,例如:response.setContentType(”text/html;charset=utf-8”);

l 請求包含大多是應用在JSP頁面中,完成多頁面的合併;

l 請求請求大多是應用在Servlet中,轉發目標大多是JSP頁面;


請求轉發與重定向比較

l 請求轉發是一個請求,而重定向是兩個請求;

l 請求轉發後瀏覽器地址欄不會有變化,而重定向會有變化,因爲重定向是兩個請求;

l 請求轉發的目標只能是本應用中的資源,重定向的目標可以是其他應用;

l 請求轉發對AServlet和BServlet的請求方法是相同的,即要麼都是GET,要麼都是POST,因爲請求轉發是一個請求;

l 重定向的第二個請求一定是GET;


三、編碼問題


  常見字符編碼:iso-8859-1(不支持中文)、gb2312、gbk、gb18030(系統默認編碼,中國的國標碼)、utf-8(萬國碼,支持全世界的編碼)


1.請求編碼

頁面中發出請求

通常向服務器發送請求數據都需要先請求一個頁面,然後用戶在頁面中輸入數據。頁面中有超鏈接和表單,通過超鏈接和表單就可以向服務器發送數據了。

因爲頁面是服務器發送到客戶端瀏覽器的,所以這個頁面本身的編碼由服務器決定。而用戶在頁面中輸入的數據也是由頁面本身的編碼決定的。

<!DOCTYPE html>
<html>
  <head>
    <title>index.html</title>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">[這就相當於給客戶端發送了響應頭content-type,指定當前頁面的編碼爲utf-8]
  </head>
  
  <body>
<form action="/hello/servlet/AServlet">
  名稱:<input type="text" name="name"/>
  <input type="submit" value="提交"/>
</form>
<a href="/hello/servlet/AServlet?name=百度">鏈接</a>
  </body>
</html>

當用戶在index.html頁面中輸入數據時,都是UTF-8列表的。因爲這個頁面本身就是UTF-8編碼的!

GET請求解碼

當客戶端通過GET請求發送數據給服務器時,使用request.getParameter()獲取的數據是被服務器誤認爲ISO-8859-1編碼的,也就是說客戶端發送過來的數據無論是UTF-8還是GBK,服務器都認爲是ISO-8859-1,這就說明我們需要在使用request.getParameter()獲取數據後,再轉發成正確的編碼。

例如客戶端以UTF-8發送的數據,使用如下轉碼方式:

<span style="font-size:18px;">String name = request.getParameter(“name”);
name = new String(name.getBytes(“iso-8859-1”), “utf-8”)</span>

再使用Tomcat時,Tomcat7以前 默認get請求是ISO-8859-1,所以在處理get請求的中文需要,先用ISO-8859-1解碼,再用UTF-8重新編碼,Tomcat8已經修正,get請求是UTF-8,不建議更改Tomcat的配置文件的方式來設置編碼,因爲用戶使用什麼服務器並不確定。

POST請求解碼

如果是POST請求,服務器可以指定編碼

當客戶端通過POST請求發送數據給服務器時,可以在使用request.getParameter()獲取請求參數之前先通過request.setCharacterEncoding()來指定編碼,然後再使用reuqest.getParameter()方法來獲取請求參數,那麼就是用指定的編碼來讀取了。

也就是說,如果是POST請求,服務器可以指定編碼!但如果沒有指定編碼,那麼默認還是使用ISO-8859-1來解讀。

request.setCharacterEncoding(“utf-8”);
String name = request.getParameter(“name”);

2.響應編碼

在使用response.getWriter()發送數據之前,一定要設置response.getWriter()的編碼,這需要使用response.setCharacterEncoding()方法:

response.setCharacterEncoding(“utf-8”);

response.getWriter().print(“百度”);

即可解決。

一般採用

response.setContentType(“text/html;charset=utf-8”);
response.getWriter().print(“百度”);

上面代碼使用setContentType()方法設置了響應頭content-type編碼爲utf-8,這不只是在響應中添加了響應頭,還等於調用了一次response.setCharacterEncoding(“utf-8”),也就是說,通過我們只需要調用一次response.setContentType(“text/html;charset=utf-8”)即可,而無需再去調用response.setCharacterEncoding(“utf-8”)了。

在靜態頁面中,使用<meta>來設置content-type響應頭,例如:

<meta http-equiv="content-type" content="text/html; charset=UTF-8">


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章