請求響應流程圖

===================
服務器處理請求的流程:
服務器每次收到請求時,都會為這個請求開辟一個新的線程。
服務器會把客戶端的請求數據封裝到request對象中,request就是請求數據的載體!
服務器還會創建response對象,這個對象與客戶端連接在一起,它可以用來向客戶端發送響應。
===================
response:其類型為HttpServletResponse
*狀態碼:200表示成功、302表示重定向、404表示客戶端錯誤(訪問的資源不存在)、500表示服務器錯誤
>sendError(int sc):發送錯誤的狀態碼,例如404、500
>sendError(int sc, String msg):發送錯誤的狀態碼+錯誤信息
>sendStatus(int sc):發送成功的狀態碼,例如302
404案例:
1 //404案例 2 @Override 3 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 4 resp.sendError(404,"您訪問的資源不存在!!!"); 5 }
*響應頭:Content-Type、Refresh、Location等等
頭就是一個鍵值對!可能會存在一個頭(一個名稱,一個值),也可能會存在一個頭(一個名稱,多個值!)
>(重要)setHeader(String name, String value):適用於單值的響應頭,例如:response.setHeader("aa","AAA");
>addHeader(String name, String value):適用於多值的響應頭
response.addHeader("aa","A");
response.addHeader("aa","AA");
response.addHeader("aa","AAA");
>setIntHeader(String name, Int value):適用於單值的int類型的響應頭
response.setIntHeader("Content-Length",888); 響應的長度
>addIntHeader(String name, int value):適用於多值的int類型的響應頭
>setDateHeader(String name, long value):適用於單值的毫秒類型的響應頭
response.setDateHeader("expires", 1000*60*60*24); 設置頁面過期時間為24小時
>addDateHeader(String name, long value):適用於多值的毫秒類型的響應頭
案例:
1)發送302,設置Location頭,完成重定向!

BServlet
1 //重定向1.設置Location頭(重定向的地址)2.響應302狀態碼 2 3 @WebServlet("/BServlet") 4 public class BServlet extends HttpServlet { 5 //重定向1.設置Location 2.發送302狀態碼 6 7 @Override 8 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 9 System.out.println("BServlet"); 10 // 請求URI s1的值:/項目名/Servlet名 11 resp.setHeader("Location","/CServlet"); 12 resp.setStatus(302); 13 } 14 }
CServlet
1 @WebServlet("/CServlet") 2 public class CServlet extends HttpServlet { 3 @Override 4 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 5 System.out.println("CServlet"); 6 } 7 }
2)定時刷新:設置Refresh頭(你可以把它理解成,定時重定向!)
DServlet
1 @WebServlet("/DServlet") 2 public class DServlet extends HttpServlet { 3 @Override 4 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 5 System.out.println("DServlet"); 6 resp.setHeader("Refresh","5;URL=/CServlet");//5秒后重定向到Cservlet 7 } 8 }
3)禁用瀏覽器緩存:Cache-Control、pragma、expires
FServlet
1 @WebServlet("/FServlet") 2 public class FServlet extends HttpServlet{ 3 @Override 4 protected void doGet(HttpServletRequest request, HttpServletResponse response){ 5 response.setHeader("Cache-Control","no-cache"); 6 response.setHeader("pragma","no-cache"); 7 response.setDateHeader("expires",-1);//過期時間為-1,也就是不緩存 8 } 9 }
在index.jsp中有

4)<meta>標簽可以替代響應頭:<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
*響應體: 通常是html、也可以是圖片!
>response的兩個流:
<>ServletOutputStream,用來向客戶端發送字節數據。ServletOutputStream out=response.getOutputStream();
<>PrintWriter,用來向客戶端發送字符數據!需要設置編碼。PrintWriter pw=response.getWriter();
<>兩個流不能同時使用!
在Interface ServletResponse接口中有方法getOutputStream()和getWriter(),如果同時使用,會拋出IllegalStateException異常
案例:(不同時)使用兩個流
1)使用ServletOutputStream字節流向客戶端寫字符串和圖片
1 @WebServlet("/GServlet") 2 public class GServlet extends HttpServlet { 3 @Override 4 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 5 /*String s="Hello outputStream 謝"; 6 byte[] bytes=s.getBytes();//將字符串轉換成字節,存儲到byte數組中 7 resp.getOutputStream().write(bytes);//字節流*/ 8 9 //響應字節數據,1.把一張圖片讀取到字節數組中2.使用字節流進行輸出 10 FileInputStream in=new FileInputStream("G://金軟軟.jpg"); 11 //讀取輸入流內容的字節到字節數組中 12 byte[] bytes1= IOUtils.toByteArray(in); 13 resp.getOutputStream().write(bytes1); 14 } 15 }
2)使用PrintWriter字符流向客戶端寫內容,需要設置字符編碼
1 @WebServlet("/HServlet") 2 public class HServlet extends HttpServlet { 3 @Override 4 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 5 resp.setContentType("text/html;charset=utf-8"); 6 resp.getWriter().write("謝軍帥"); 7 resp.getWriter().print("<br>金泰妍"); 8 } 9 }
*重定向:設置302,設置Location!其中變化的只有Location,所以Java提供了一個快捷方法,完成重定向1
>sendRedirect(String location)方法
1 //快捷 2 resp.sendRedirect("http://www.baidu.com");
request:封裝了客戶端所有的請求數據!
請求行
請求頭
空行
請求體(GET沒體)
*獲取常用信息
>獲取客戶端IP,request.getRemoteAddr()
>請求方式,request.getMethod(),可能時POST或GET
*獲取請求頭
>(重要)String getHeader(String name),適用於單值頭
>int getInHeader(String name),適用於單指int類型的請求頭
>long getDateHeader(String name),適用於單值毫秒類型的請求頭
>Enumeration<String> getHeaders(String name),適用於多值請求頭
通過User-Agent識別用戶瀏覽器類型
1 //演示:獲取客戶端的IP地址、獲取請求方式、獲取User-Agent,得到客戶端的信息(操作系統,瀏覽器) 2 @WebServlet("/Servlet2") 3 public class Servlet2 extends HttpServlet { 4 @Override 5 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 6 String remoteAddr = req.getRemoteAddr(); 7 System.out.println("(IPV6)IP:"+remoteAddr); 8 String method = req.getMethod(); 9 System.out.println("請求方式:"+method); 10 String userAgent = req.getHeader("User-Agent"); 11 System.out.println("userAgent:"+userAgent); 12 //先把字符串轉換成小寫,,然后再看是否包含 13 if(userAgent.toLowerCase().contains("chrome")){ 14 System.out.println("您好"+remoteAddr+"您使用的是谷歌瀏覽器"); 15 }else if (userAgent.toLowerCase().contains("firefox")){ 16 System.out.println("您好"+remoteAddr+"您使用的是火狐瀏覽器"); 17 }else if (userAgent.toLowerCase().contains("msie")){ 18 System.out.println("您好"+remoteAddr+"您使用的時IE瀏覽器"); 19 }else{ 20 System.out.println("不曉得你用的啥"); 21 } 22 } 23 }
防盜鏈:如果請求不是通過本站的超鏈接發出的,發送錯誤狀態碼404。Refresh這個請求頭,表示請求的來源!(比如:從一個頁面跳轉到另一個頁面,都會有Refresh值)
1 @WebServlet("/Servlet3") 2 public class Servlet3 extends HttpServlet { 3 @Override 4 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { 5 /*使用Refresh請求頭,來防盜鏈*/ 6 String referer = req.getHeader("Referer"); 7 System.out.println(referer); 8 if(referer == null || !referer.contains("localhost")){//如果前面的滿足的話,后邊的就不用判斷了 9 resp.sendRedirect("http://www.baidu.com"); 10 }else{ 11 System.out.println("hello"); 12 } 13 } 14 } 15 /* 16 Referer這個頭可以得到請求的來源; 17 1.在地址欄中請求的話其值為null 18 2.在其它頁面以鏈接的形式請求的話其值為URL 19 */
*獲取請求URL
http://localhost:8080/day10_2/AServlet?username=xxx&password=yyy
>String getScheme():獲取協議,http
>String getServerName():獲取服務器名,localhost
>String getServerPort():獲取服務器端口,8080
>String getContextPath():獲取項目名(會經常使用,重要),/day10_2
>String getServletPath():獲取Servlet路徑,/AServlet
>String getQueryString():獲取參數部分,即問號后面的部分,username=xxx&password=yyy
>String getRequestURI():獲取請求URI,等於項目名+Servlet路徑,/day10_2/AServlet
>String getRequestURL():獲取請求URL,等於不包括參數的整個請求路徑,http://localhost:8080/day10_2/AServlet
*獲取請求參數:請求參數是由客戶端發送給服務器的!有可能是在請求體中(POST),也可能在URL之后(GET)
>***String getParameter(String name):獲取指定名稱的請求參數值,適用於單值請求參數
>String[] getParameterValues(String name):獲取指定名稱的請求參數值,適用於多值請求參數
>Enumeration<String> getParameterNames():獲取所有請求參數的名稱
>***Map<String,String[] > getParameterMap():獲取所有的請求參數,其中key為參數名,value為參數值。將請求的參數封裝到Map中
案例:超鏈接參數
案例:表單數據
*請求轉發和請求包含---dispatcher的英文意思是調度員,來調用別的Servlet
RequestDispatcher rd=request.getRequestDispatcher("/MyServlet");---->使用request獲取RequestDispatcher對象,括號里的參數是被轉發或包含的Servlet的servlet路徑
請求轉發:***rd.forward(request,response);
請求包含:rd.include(request,response);

有時一個請求需要多個Servlet協作才能完成,所以需要在一個Servlet跳到另一個Servlet!
>一個請求跨多個Servlet,需要使用轉發和包含。
>請求轉發:由下一個Servlet完成響應體!當前Servlet可以設置響應頭!(留頭不留體)
>請求包含:由兩個Servlet共同來完成響應體!(都留)
>無論是請求轉發還是請求包含,都在一個請求范圍內!使用同一個request和response!
請求轉發示例:
OneServlet:
1 public class OneServlet extends HttpServlet { 2 3 public void doGet(HttpServletRequest request, HttpServletResponse response) 4 throws ServletException, IOException { 5 System.out.println("OneServlet..."); 6 response.setHeader("aa", "AA");//頭 7 response.getWriter().print("hello OneServlet!");//體 8 9 //轉發 10 RequestDispatcher rd=request.getRequestDispatcher("/TwoServlet"); 11 rd.forward(request, response); 12 } 13 }
TwoServlet:
1 public class TwoServlet extends HttpServlet { 2 public void doGet(HttpServletRequest request, HttpServletResponse response) 3 throws ServletException, IOException { 4 System.out.println("TwoServlet..."); 5 response.getWriter().print("hello TwoServlet"); 6 } 7 }
在瀏覽器中訪問http://localhost:8080/XJS_Servlet3/OneServlet
控制台結果:
OneServlet... TwoServlet...
瀏覽器頁面結果:
還有OneServlet設置的頭也發送到瀏覽器了
hello TwoServlet
證明了OneServlet留頭不留體
請求包含示例:
Servlet1:
1 public class Servlet1 extends HttpServlet { 2 3 public void doGet(HttpServletRequest request, HttpServletResponse response) 4 throws ServletException, IOException { 5 System.out.println("Servlet1..."); 6 response.setHeader("aa", "AA");//頭 7 response.getWriter().print("hello OneServlet!");//體 8 9 //轉發 10 RequestDispatcher rd=request.getRequestDispatcher("/include/Servlet2"); 11 rd.include(request, response); 12 } 13 }
Servlet2:
1 public class Servlet2 extends HttpServlet { 2 3 public void doGet(HttpServletRequest request, HttpServletResponse response) 4 throws ServletException, IOException { 5 System.out.println("TwoServlet..."); 6 response.getWriter().print("hello TwoServlet"); 7 } 8 9 }
請求http://localhost:8080/XJS_Servlet3/include/Servlet1的結果:
控制台:
Servlet1... TwoServlet...
瀏覽器頁面:
hello OneServlet!hello TwoServlet
結果證明:include留頭又留體
*request域
Servlet中三大域對象:request、session、application,都有如下三個方法:
>void setAttribute(String name, Object value)
>Object getAttribute(String name)
>void removeAttribute(String name)
>同一請求范圍內使用request.setAttribute()、request.getAttribute()來傳值!前一個Servlet調用setAttribute()來保存值,后一個Servlet調用getAttribute()獲取值。
*請求轉發和重定向的區別
>請求轉發是一個請求一次響應,而重定向是兩次請求兩次響應
>請求轉發地址欄不變化,而重定向會顯示后一個請求的地址
>請求轉發只能轉發到本項目的其他Servlet,而重定向不只能重定向到本項目的其他Servlet,還能定向到其他項目
>請求轉發是服務器端行為,只需給出轉發的Servlet路徑,而重定向需要給出requestURL,即包含項目名!!!
>請求轉發和重定向效率是轉發高!因為是一個請求!
<>需要地址欄發生變化,那么必須使用重定向!
<>需要在下一個Servlet中獲取request域中的數據,必須使用轉發!
編碼
常見字符編碼:iso-8859-1(不支持中文)、gbk(系統默認編碼,中國的國標碼)、utf-8(萬國碼,支持全世界的編碼,所以我們使用這個)
1.響應編碼

response.setCharacterEncoding("utf-8");//設置服務器的編碼為utf-8
response.setHeader("Content-Type","text/html;charset=utf-8");
//設置響應頭,服務器編碼格式為utf-8
response設置Content-Type的快捷方法:
response.setContentType("text/html;charset=utf-8");
想不亂碼:在使用getWriter()方法之前,先調用下面方法:
***response.setContentType("text/html;charset=utf-8");
2.請求編碼---所有的亂碼都有解決的方法(先在Servlet中獲取參數,然后在對參數進行反編碼)
一般請求參數,在Servlet中獲取時不會出現亂碼,如果出現亂碼就按照下面方法解決亂碼:

*客戶端發送給服務器的請求參數是什么編碼:
客戶端首先要打開一個頁面,然后在頁面中提交表單或點擊超鏈接!在請求這個頁面時,服務器響應的編碼是什么,那么客戶端發送請求時的編碼就是什么
*服務器端默認使用什么編碼來解碼參數:
(tomcat8之前的)服務器端默認使用ISO-8859-1來解碼!所以這一定會出現亂碼的!因為iso不支持中文!
在Servlet中設置request.setCharacterEncoding("utf-8");---然后在獲取請求參數,可以防止亂碼
Tomcat8默認編碼為UTF-8

3.URL編碼
表單的類型:Content-Type:application/x-www-form-urlencoded,就是把中文轉換成%后面跟隨兩位的16進制。
為什么要用它:在客戶端和服務器之間傳遞中文時需要把它轉換成網絡適合的方式。
*它不是字符編碼!
*它是用來在客戶端和服務器端之間傳遞參數用的一種方式!
*URL編碼需要先指定一種字符編碼,把字符串解碼后,得到byte[] ,然后把小於0的字節+256,再轉換成16進制。前面加一個%。
*POST請求默認就使用URL編碼!tomcat會自動使用URL解碼!
*URL編碼:String username=URLEncoder.encode(username,"utf-8");
*URL解碼:String username=URLDecoder.decode(username,"utf-8");
1)GET請求中的中文沒有URL編碼,可能會出現丟失字節
2)使用的是表單,表單自動使用URL編碼
3)服務器會自動識別URL編碼,然后自動做URL解碼
4.路徑
*web.xml中<url-pattern>路徑,(叫它Servlet路徑!)
>要么以 “*” 開頭,要么以 “/” 開頭。
*轉發和包含路徑
>以 “/”開頭:相對當前項目的路徑,例如:http://localhost:8080/項目名/ request.getRequestdispacher("/BServlet").forward(request,response);
>不以 “/” 開頭:相對當前Servlet路徑
*重定向路徑(客戶端路徑)
>以“ /”開頭:相對於當前主機,例如:http://localhost:8080/,所以需要自己手動添加項目名
*頁面中超鏈接和表單路徑
>與重定向相同,都是客戶端路徑!需要添加項目名
><form action="/XJS_Servlet3/AServlet">
><a href="/XJS_Servlet3/AServlet">
>****建議使用以 “/” 開頭的路徑,即絕對路徑!
*ServletContext獲取資源路徑
>相對當前項目目錄,即index.jsp所在目錄。
*ClassLoader獲取資源路徑
>相對classes目錄
>ClassLoader獲取資源時,不能以“/”開頭!
*Class獲取資源路徑
>以“/”開頭相對classes目錄
>不以"/"開頭相對當前.class文件所在目錄。
