1. 程式人生 > >請求和響應

請求和響應

location oct 求一個 不變 pragma 部分 情況 區別 password

技術分享

服務器處理請求的流程:

    • 服務器每次收到請求時,都會為這個請求開辟一個新的線程。
    • 服務器會把客戶端的請求數據封裝到request對象中,request就是請求數據的載體!
    • 服務器還會創建對象,這個對象與客戶端連接在一起,它可以用來向客戶端發送響應。

一、HttpServletResponse對象

  1、發送狀態碼相關的方法

  ServletResponse:與協議無關的類型。

  HttpServletResponse:與協議相關的類型

狀態碼:200表示成功、302表示重定向、404表示客戶端錯誤(訪問資源不存在)、500表示服務器端錯誤
  • sendError(int sc) :發送錯誤狀態碼,例如404,500
  • sendError(int sc,String msg):發送錯誤狀態碼,還可以帶一個錯誤信息
  • setStatus(int sc):發送成功的狀態碼,可以用來發送302
案例:
@WebServlet(name = "AServlet" ,urlPatterns = "/Aservlet")
public class AServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
      throws ServletException, IOException { response.sendError(404,"資源存在,但還發你404"); } }
  2、響應頭:Content-Type、Refresh、Location等等
  • setHeader(String name,String value):適用於單值的響應頭,例如
response.setHeader("aaa","AAA");
  • addHeader(String name,String value):適用於多值的響應頭,例如:
response.addHeader("aaa","ddd"); response.addHeader("aaa","ccc"); response.addHeader("aaa","fff");
  • 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):適用於多值得毫秒類型的響應頭
    常用setHeader(String name,String value). 案例:
    • 發送302,設置Location頭,完成臨時重定向!
/*
* 演示重定向
* 用戶請求BServlet,然後BServlet響應302,給出Location頭
* */
@WebServlet(name = "BServlet",urlPatterns = "/BServlet")
public class BServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("BServlet");
        /*
        * 重定向:
        * 1、設置Location
        * 2、發送302狀態碼
        * */
        response.setHeader("Location","/CServlet");
        response.sendError(302);
    }
}
/*
* 瀏覽器會重定向到這來
* */
@WebServlet(name = "CServlet",urlPatterns = "/CServlet")
public class CServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("CServlet");
}
}
// BServlet響應頭

HTTP/1.1 302
Location: /CServlet
Content-Length: 0
Date: Wed, 30 Aug 2017 01:57:17 GMT

//BServlet請求頭

GET /BServlet HTTP/1.1
Host: localhost:8080
User-Agent: *****
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Cookie: Idea-e96526d7=db12919d-58f1-479a-b0f7-3104911c767b; Webstorm-2933ea9e=87a0f860-7465-47bb-8d57-4358bd45ea39; JSESSIONID=AD5DC7F501C87B36FF2186A0A1596564
Connection: keep-alive
Upgrade-Insecure-Requests: 1

//CServlet響應頭

HTTP/1.1 200
Content-Length: 0
Date: Wed, 30 Aug 2017 01:57:17 GMT

//CServlet請求頭

GET /CServlet HTTP/1.1
Host: localhost:8080
User-Agent: *****
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Cookie: Idea-e96526d7=db12919d-58f1-479a-b0f7-3104911c767b; Webstorm-2933ea9e=87a0f860-7465-47bb-8d57-4358bd45ea39; JSESSIONID=AD5DC7F501C87B36FF2186A0A1596564
Connection: keep-alive
Upgrade-Insecure-Requests: 1

    • 定時刷新,設置Refresh頭,可以理解為定時重定向。
/*
* 演示定時刷新
* 設置一個Refresh,表示定時刷新
* */
@WebServlet(name = "DServlet",urlPatterns = "/DServlet")
public class DServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        /*
        * 下面是用來發送響應體
        * */
        PrintWriter writer = response.getWriter();
        writer.print("歡迎xxx登陸!5秒鐘後會自動跳轉到主頁!亂碼來的");
        //設置名為Refresh的響應頭
        response.setHeader("Refresh","5;URL=/EServlet");
    }
}
    • 禁止瀏覽器緩存:Cache-Control、pragma、expires
/*
* 禁用瀏覽器緩存
* */
@WebServlet(name = "FServlet",urlPatterns = "/FServlet")
public class FServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setHeader("Cache-Control","no-cache");
        response.setHeader("Pragma","no-cache");
        response.setDateHeader("Expires",-1);
    }
}
    • <meta>標簽可以替代響應頭:<meta http-equiv="Content-Type" content="text/html";charset=UTF-8>
3、響應體:通常是html、也可以是圖片   response的兩個流:
    • ServletOutputStream,用來向客戶端發送字節數據, ServletOutputStream out = response.getOutputStream();
    • PrintWriter,用來向客戶端發送字符數據,需要設置編碼, PrintWriter writer = respones.getWriter();
    • 兩個流不能同時使用。
案例: 使用PrintWriter發送字符數據 使用ServletOutputStream發送字節數據(圖片) 重定向:設置302,設置Location,其中變化的只有Location,所以Java提供了一個快捷方法,完成重定向。 sendRedirect(String location)方法 response.sendRedirect("http://www.baidu.com"); 二、HttpServletRequest對象   請求協議中的數據都可以通過request對象來獲取,request封裝了客戶端所有的請求數據,GET無請求體。

  1、獲取常用信息

    • 獲取客戶端IP,案例:封IP,request.getRemoteAddr();
    • 請求方式,request.getMethod(),POST或GET

  2、獲取請求頭

    • String getHeader(String name):適用於單值頭
    • int getIntHeader(String name):適用於單值int類型的請求頭
    • long getDateHeader(String name):適用於單值毫秒類型的請求頭
    • Enumeration<String> getHeaders(String name):適用於多值請求頭

  案例:

    • 通過User-Agent識別用戶瀏覽器類型
 1 @WebServlet(name = "AServlet",urlPatterns = "/AServlet")
 2 public class AServlet extends HttpServlet {
 3     protected void doGet(HttpServletRequest request, HttpServletResponse response)
 4             throws ServletException, IOException {
 5         String addr = request.getRemoteAddr();
 6         System.out.println("IP:"+addr);
 7         System.out.println("METHOD:"+request.getMethod());
 8         String userAgent = request.getHeader("User-Agent");
 9 //        System.out.println(userAgent);
10        // Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36
11         //是否包含Chrome,如果包含,說明用戶使用的是google瀏覽器
12         if(userAgent.toLowerCase().contains("chrome")){
13             System.out.println("您好:"+addr+",您用的是谷歌");
14         }else if (userAgent.toLowerCase().contains("firefox")){
15             System.out.println("您好:"+addr+",您用的是火狐");
16         }
17     }
    • 防盜鏈:如果請求不是通過本站的超鏈接發出的,發送錯誤狀態碼404。Referer這個請求頭表示請求的來源。
@WebServlet(name = "BServlet",urlPatterns = "/BServlet")
public class BServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        /*
        * 使用Referer請求頭,來防盜鏈
        * 直接在地址欄輸入,則Referer頭值為null
        * */
        String referer = request.getHeader("Referer");
        System.out.println(referer);
        if (referer == null || !referer.contains("localhost")){
            response.sendRedirect("http://www.baidu.com");
        }else {
            System.out.println("hello");
        }
    }
}

  3、獲取請求URL

    http://localhost:8080/day10_2/AServlet?username=xx&password=yyy

  • String getScheme():獲取協議,http
  • String getServerName():獲取服務器名,localhost
  • String getServerPost():獲取服務器端口,8080
  • String getContextPath():獲取項目名, /day10_2
  • String getServletPath():獲取Servlet路徑, /AServlet
  • String getQueryString():獲取參數部分,即問號後面的部分,username=xx&password=yyy
  • String getRequestURI():獲取請求URI,等於項目名+Servlet路徑,/day10_2/AServlet
  • String getRequestURL():獲取請求URL,等於不包含參數的整個請求路徑,http://localhost:8080/day10_2/AServlet

  4、獲取請求參數:請求參數:請求參數是由客戶端發送給服務的,有可能是在請求體中(POST),也有可能是在URL之後(GET)。

  • String getParameter(String name):獲取指定名稱的請求參數值,適用於單值請求參數
  • String[] getParamerValues(String name):獲取指定名稱的請求參數值,適用於多值請求參數
  • Enumeration<String> getParameterName():獲取所有請求參數名稱
  • Map<String,String[]> getParameterMap():獲取所有請求參數,其中key為參數名,value為參數值。
  • 案例:超鏈接參數
  • 案例:表單數據
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8 <h1>測試請求參數</h1>
 9 <a href="/AServlet?xxx=XXX&yyy=YYY">點擊這裏</a>
10 <hr/>
11 <form action="/AServlet" method="POST">
12     用戶名:<input type="text" name="username"/><br/>
13     密碼:<input type="password" name="password"/><br/>
14     愛好:<input type="checkbox" name="hobby" value="dq"/>打球
15     <input type="checkbox" name="hobby" value="ms"/>美食
16     <input type="checkbox" name="hobby" value="ks"/>看書
17     <br/>
18     <input type="submit" value="提交"/>
19 </form>
20 </body>
21 </html>
 1 /*
 2 * 演示request獲取請求參數!
 3 * */
 4 @WebServlet(name = "AServlet",urlPatterns = "/AServlet")
 5 public class AServlet extends HttpServlet {
 6     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 7         String username = request.getParameter("username");
 8         String password = request.getParameter("password");
 9         String[] hobby = request.getParameterValues("hobby");
10         System.out.println(username+","+password+","+ Arrays.toString(hobby));
11         /*
12         * 測試獲取所有請求參數的名稱
13         * */
14         Enumeration names = request.getParameterNames();
15         while (names.hasMoreElements()) {
16             System.out.println(names.nextElement());
17         }
18         /*
19         * 獲取所有請求參數,封裝到Map中
20         * */
21         Map<String,String[]> map = request.getParameterMap();
22         for (String name : map.keySet()) {
23             String[] values = map.get(name);        System.out.println(name+"="+Arrays.toString(values));
24         }
25         System.out.println("hello");
26     }
27     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
28         System.out.println("GET:"+request.getParameter("xxx"));
29         System.out.println("GET:"+request.getParameter("yyy"));
30     }
31 }

  5、請求轉發和請求包含:

    RequestDispatcher rd = request.getRequestDispatchet("/MyServlet"); 使用request獲取RequestDisapatcher對象,方法的參數是被轉發或包含的Servlet的Servlet路徑      

  • 請求轉發:rd.forward(request,response);
  • 請求包含:rd.include(request,response);

    有時一個請求需要多個Servlet協作才能完成,所以需要在一個Servlet跳到另一個Servlet!

技術分享

    • 一個請求跨多個Servlet,需要使用轉發和包含。
    • 請求轉發:由下一個Servlet完成響應體,當前Servlet可以設置響應頭(留頭不留體);
    • 請求包含:由兩個Servlet共同完成響應體(都留)。
    • 無論是請求轉發還是請求包含,都在一個請求範圍內,使用同一request和response。

  6、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()獲取

    技術分享

  7、請求轉發和重定向的區別

    • 請求轉發是一個請求一個響應,而重定向是兩個請求兩次響應
    • 請求轉發地址欄不變化,而重定向會顯示後一個請求的地址
    • 請求轉發只能轉發到本項目其他Servlet,而重定向不止重定向到本項目的其他Servlet,還能定向到其他項目
    • 請求轉發是服務器端行為,只需給出轉發的Servlet路徑,而重定向需要給出requestURI,即包含項目名。
    • 請求轉發和重定向效率是轉發高,因為是一個請求。
      • 需要地址欄變化,那麽必須重定向
      • 需要在下一個Servlet中獲取request域中的數據,必須要使用轉發。
/*
* 演示請求轉發和包含
* 註意在一個Tomcat中不能有name、urlPatterns名稱相同的情況,否則拋異常
* */
@WebServlet(name = "OneServlet",urlPatterns = "/OneServlet")
public class OneServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("OneServlet");
        response.setHeader("aaa","AAA");//設置響應頭
      /*
      * 向request域中添加一個屬性
      * */
     request.setAttribute("username","zhangsan")
     response.getWriter().print("hello OneServlet");//設置響應體,包含時顯示,轉發時不顯示 request.getRequestDispatcher("/TwoServlet").include(request,response);//請求包含
      //等同於調用TwoServlet的service()方法
      //request.getRequestDispatcher("/TwoServlet").forward(request,response);//請求轉發
  } 
}
@WebServlet(name = "IncludeTwoServlet",urlPatterns = "/include/TwoServlet")
public class TwoServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
        throws ServletException, IOException {
     System.out.println(request.getAttribute("username"));
     System.out.println("TwoServlet...");
        response.getWriter().print("hello TwoServlet");//設置響應體
    }
}

三、編碼

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

1、響應編碼

  • 當使用response.getWriter()來向客戶端發送字符數據時,如果在之前沒有設置編碼,那麽默認使用iso,因為iso不支持中文,一定亂碼;
  • 在使用response.getWriter()之前可以使用response.setCharacterEncoding()來設置字符流的編碼為gbk或utf-8,當然我們通常選擇utf-8,這樣發送出去的字符都使用了設置的編碼方式。
  • 在使用response.getWriter()之前可以使用response.setHeader("Content-type","text/html;charset=utf-8")來設置響應頭,通知瀏覽器這邊使用的是utf-8,瀏覽器通過Content-Type頭知道,瀏覽器也使用utf-8。
  • setHeader("Content-type","text/html;charset=utf-8")的快捷方法是:setContentType("text/html;charset=utf-8")。

技術分享

2、請求編碼

  • 客戶端發送給服務器的請求參數是什麽編碼:客戶端首先打開一個頁面,然後再頁面中提交表單或點擊超鏈接,在請求這個頁面時,服務器響應的編碼是什麽,那麽客戶端發送請求時的編碼就是什麽
  • 服務器端默認使用什麽編碼來解碼參數:服務器端默認ISO-8859-1來解碼,一定出現亂碼。
  • 請求編碼處理分為兩種:GET、POST,GET請求參數不在請求體中,而POST請求參數在請求體中,所有他們的處理方式是不同的!
  • GET請求編碼處理:
    • String username = new String(request.getParameter("iso-8859-1","utf-8"));
    • 在server.xml中配置URIEncoding=utf-8
  • POST請求編碼處理:
    • String username = new String(request.getParameter("iso-8859-1","utf-8"));
    • 在獲取參數之前調用request.setCharacterEncoding(“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 = URLEncoder.decode(username,"utf-8");

技術分享

  我們需把鏈接中的中文參數,使用url來編碼,使用jsp,因為HTML不能給出Java代碼。

4、路徑

  • web.xml中<url-pattern>路徑(叫它Servlet路徑)
    • 要麽以“*”開頭,要麽以“/”開頭
  • 轉發和包含路徑
    • 以“/”開頭:相對當前項目路徑,例如:http://localhost:8080/項目名/
    • 不以“*”開頭:相對當前Servlet路徑。
  • 重定向路徑(客戶端路徑)
    • 以“/”開頭:相對於當前主機,例如:http://localhost:8080/,所以需要自己手動添加項目名
  • 頁面中超鏈接和表單路徑
    • 與重定向相同,都是客戶端路徑,需要添加項目名
  • ServletContext獲取資源路徑
    • 相對當前項目目錄,即當前index.jsp所在目錄
  • ClassLoader獲取資源路徑
    • 相對classes目錄
  • Class獲取資源路徑
    • 以“/”開頭相對classes目錄
    • 不以“/”開頭相對當前.class文件所在目錄。

請求和響應