Java Web請求和響應機制


請求響應流程圖

=================== 

服務器處理請求的流程:

  服務器每次收到請求時,都會為這個請求開辟一個新的線程。

  服務器會把客戶端的請求數據封裝到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文件所在目錄。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM