JavaWeb Request和Response


1. RequestResponse

1.1. Web應用運行機制

到目前為止,我們已經掌握了Web應用程序的運行機制,現在學習的就是Web應用程序運行機制中很重要的內容 —— RequestResponse

首先,我們先來看一看RequestResponseWeb應用程序運行時,是怎么樣的。

 

通過上圖的Web應用程序運行機制,我們可以知道關於RequestResponse的信息:

  • Web應用程序接收一次請求,就創建一個Request對象和Response對象。
  • 通過Request對象可以在服務器端獲取客戶端發送的請求數據內容。
  • 通過Response對象可以生成服務器端向客戶端響應的數據內容。
  • Request對象和Response對象並不是Web應用程序所創建的,而是由Tomcat服務器創建(JavaEE並沒有RequestResponse的實現類)。
  • JavaEE提供了javax.servlet.http包中提供了HttpServletRequestHttpServletResponse接口,這兩個接口是繼承於javax.servlet包中的ServletRequestServletResponse接口。
  • javax.servlet包中的ServletRequestServletResponse接口是與協議無關的,而javax.servlet.http包中的HttpServletRequestHttpServletResponse接口是與Http協議有關的。

1.2. 百度百科

  • Request

Request這個對象不用事先聲明,就可以在JSP網頁中使用,在編譯為Servlet之后,它會轉換為javax.servlet.http.HttpServletRequest形態的對象,HttpServletRequest對象是有關於客戶端所發出的請求的對象,只要是有關於客戶端請求的信息,都可以藉由它來取得,例如請求標頭、請求方法、請求參數、客戶端IP,客戶端瀏覽器等等信息。

  • Response

Response對象用於動態響應客戶端請示,控制發送給用戶的信息,並將動態生成響應。Response對象只提供了一個數據集合cookie,它用於在客戶端寫入cookie值。若指定的cookie不存在,則創建它。若存在,則將自動進行更新。結果返回給客戶端瀏覽器。

2. 掌握Response

2.1. Response概述

ResponseWeb應用程序用來封裝向客戶端響應信息的,是Servlet接口的service()方法的一個參數,類型為javax.servlet.http.HttpServletResponse。客戶端每次發送請求時,服務器都會創建一個Response對象,並傳遞給Servlet接口的service()方法,來完成向客戶端的響應工作。

下列是javax.servlet.http.HttpServletResponse常用的API列表:

Method Summary

 void

addDateHeader(String name, long date) 
          Adds a response header with the given name and date-value.

 void

addHeader(String name, String value) 
          Adds a response header with the given name and value.

 void

sendRedirect(String location) 
          Sends a temporary redirect response to the client using the specified redirect location URL.

 void

setDateHeader(String name, long date) 
          Sets a response header with the given name and date-value.

 void

setHeader(String name, String value) 
          Sets a response header with the given name and value.

 void

setStatus(int sc) 
          Sets the status code for this response.

下列是javax.servlet.ServletResponse常用的API列表:

Method Summary

 ServletOutputStream

getOutputStream() 
          Returns a ServletOutputStream suitable for writing binary data in the response.

 PrintWriter

getWriter() 
          Returns a PrintWriter object that can send character text to the client.

 void

setCharacterEncoding(String charset) 
          Sets the character encoding (MIME charset) of the response being sent to the client, for example, to UTF-8.

 void

setContentType(String type) 
          Sets the content type of the response being sent to the client, if the response has not been committed yet.

針對HttpServletResponseServletResponse常用的API,我們進行逐一討論。

2.2. Response重定向

在學習Http響應協議時,我們曾做過重定向案例,但那時我們並不清楚其原理,下面我們就討論一下利用HttpServletResponse來完成重定向的功能。

  • 創建一個Servlet來完成重定向功能。
public class ResponseServlet1 extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setStatus(302);
        response.setHeader("Location", "/response/index.html");
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}
  • Web工程的web.xml文件中注冊Servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <servlet>
    <servlet-name>ResponseServlet1</servlet-name>
    <servlet-class>app.java.response.ResponseServlet1</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>ResponseServlet1</servlet-name>
    <url-pattern>/response1</url-pattern>
  </servlet-mapping>    
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>
  • 創建一個HTML頁面。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>index.html</title>
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="this is my page">
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <h1>Request與Response內容</h1>
  </body>
</html>
  • Web工程發布到Tomcat服務器,並啟動Tomcat服務器。
  • 打開瀏覽器,在地址欄中輸入http://localhost:8080/response/response1。

通過上述案例,我們可以發現在重定向中,實際上客戶端向服務器端發送了兩次請求,具體步驟如下:

  • 在瀏覽器地址輸入URL,訪問服務器端的Servlet,這是第一次發送的請求。
  • 服務器端接收到客戶端的請求后,由Servlet做出處理,重定向到index.html頁面,並響應給客戶端瀏覽器。
  • 客戶端瀏覽器接收到服務器端的響應后,再次發送第二次請求。
  • 服務器端接收到客戶端的第二次請求后,並響應index.html給客戶端瀏覽器顯示。

其實Response對象提供了sendRedirect(String location)方法,同樣可以完成重定向的工作。

public class ResponseServlet1 extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.sendRedirect("/09_request&response/response/index.html");
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
} 

2.3. 登錄錯誤案例

利用Response對象提供了sendRedirect(String location)方法可以完成重定向的功能,實現登錄功能中如果錯誤的案例。具體實現步驟如下:

  • 創建一個用於登錄的HTML頁面。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>login.html</title>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <h1>登錄案例:登錄錯誤重定向回當前頁面</h1>
    <form id="form1" action="/response/response2" method="post">
        用戶名:<input type="text" name="username"><br>
        密碼:<input type="password" name="password"><br>
        <input type="submit" value="登錄">
    </form>
  </body>
</html>
  • 創建一個Servlet用於處理登錄是否成功邏輯。
public class ResponseServlet2 extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 獲取客戶端瀏覽器提交的用戶名與密碼內容
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        //模擬查詢數據庫:admin/admin
        if(username.equals("admin")&&password.equals("admin")){
            // 登錄成功
            response.setContentType("text/html;charset=utf-8");
            PrintWriter out = response.getWriter();
            out.println("<h1>登錄成功</h1>");
        }else{
            // 登錄錯誤
    response.sendRedirect("/response/response/login.html");
        }
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
} 
  • Web工程的web.xml文件中注冊Servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <servlet>
    <servlet-name>ResponseServlet2</servlet-name>
    <servlet-class>app.java.response.ResponseServlet2</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>ResponseServlet2</servlet-name>
    <url-pattern>/response2</url-pattern>
  </servlet-mapping>    
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>
  • Web工程發布到Tomcat服務器,並啟動Tomcat服務器。
  • 打開瀏覽器,在地址欄中輸入http://localhost:8080/response/response/login.html

2.4. 自動刷新案例

在學習Http響應協議時,我們曾做過自動刷新案例,但那時我們並不清楚其原理,下面我們就討論一下利用HttpServletResponse來完成自動刷新的功能。

  • 創建一個Servlet用於設置5秒鍾后自動刷新頁面的功能(自動跳轉到登錄頁面)。
public class ResponseServlet3 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setHeader("refresh", "5;url=/response/response/login.html");
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();
        out.println("當前頁面會在5秒鍾后自動跳轉到登錄頁面.");
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
} 
  • Web工程的web.xml文件中注冊Servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <servlet>
    <servlet-name>ResponseServlet3</servlet-name>
    <servlet-class>app.java.response.ResponseServlet3</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>ResponseServlet3</servlet-name>
    <url-pattern>/response3</url-pattern>
  </servlet-mapping>    
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>
  • Web工程發布到Tomcat服務器,並啟動Tomcat服務器。
  • 打開瀏覽器,在地址欄中輸入http://localhost:8080/response/response3

在學習HTML技術的時候,我們知道在head標簽中有meta標簽,該標簽同樣可以完成自動刷新頁面的功能。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>refresh.html</title>
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="this is my page">
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <meta http-equiv="refresh" content="5;url=/response/response/login.html">
  </head>
  <body>
    <h1>當前頁面會在5秒鍾后自動跳轉到登錄頁面.</h1>
  </body>
</html>

使用Response對象的setHeader()方法與HTML頁面的<meta>標簽,同樣可以完成頁面自動刷新功能,但是兩者是有區別的:

  • 使用Response對象的setHeader()方法:refresh信息是顯示在響應頭信息中。
  • 使用HTML頁面的<meta>標簽:refresh信息是顯示在響應體信息中。

在上述基礎上,完成動態效果的倒計時功能,需要使用javascript技術來完成。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>refresh.html</title>
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="this is my page">
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <meta http-equiv="refresh" content="5;url=/response/response/login.html">
  </head>
  <script type="text/javascript">
      var times = 5;
      function init(){
        document.getElementById("times").innerHTML = times;
        times--;
        setTimeout("init()",1000);
    }
  </script>
  <body onload="init();">
    <h1>當前頁面會在<span id="times"></span>秒鍾后自動跳轉到登錄頁面</h1>
  </body>
</html>

2.5. 禁止瀏覽器緩存案例

在學習Http響應協議時,我們知道響應協議中有三個頭信息可以禁止瀏覽器本地緩存,分別是Cache-Control、Pragma和Expires。下面我們就通過一個案例來討論一下。

  • 創建一個Servlet用來向客戶端瀏覽器響應一些數據內容。
public class ResponseServlet4 extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PrintWriter out = response.getWriter();
        out.println("當前時間是:"+new Date().toString());
        System.out.println("已經成功地訪問了當前Servlet...");
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
} 
  • Web工程的web.xml文件中注冊Servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <servlet>
    <servlet-name>ResponseServlet4</servlet-name>
    <servlet-class>app.java.response.ResponseServlet4</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>ResponseServlet4</servlet-name>
    <url-pattern>/response4</url-pattern>
  </servlet-mapping>    
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>
  • 打開瀏覽器,選擇“工具”->Internet選項”->“瀏覽歷史記錄”中的“設置”->“查看文件”,清空所有緩存內容。
  • 在瀏覽器在地址欄中輸入http://localhost:8080/response/response4進行訪問。
  • 這時再查看瀏覽器緩存內容的文件夾,會發現產生了這次訪問的緩存內容。
  • 這時再查看控制台信息,會發現打印了相關內容,說明客戶端成功訪問了服務器端的Servlet
  • 這時再次訪問http://localhost:8080/response/response4地址,會發現內容不再變化,也不會產生請求和響應內容。

由於IE瀏覽器的本地緩存問題,第二次再次訪問相同Servlet攔截路徑時,不會再訪問服務器端的Servlet,而是訪問本地緩存內容。要想每次客戶端訪問都訪問到服務器端的Servlet的話,我們需要禁止瀏覽器緩存機制。

  • 我們可以通過響應頭信息中的三個相關內容來設置禁止瀏覽器緩存。
public class ResponseServlet4 extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
        //設置響應頭信息,禁止瀏覽器緩存.
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Pragma", "no-cache");
        response.setDateHeader("Expires", -1);
        PrintWriter out = response.getWriter();
        out.println("<h1>已經成功地訪問了當前Servlet...</h1>");
     }
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
} 
  • 重新發布Web工程到Tomcar服務器,並重啟Tomcat服務器。
  • 打開瀏覽器,清空本地緩存內容,在地址欄中輸入相同URL地址。
  • 但是,現在查看瀏覽器本地緩存文件夾,會發現沒有任何緩存內容。
  • 這時再次訪問http://localhost:8080/response/response4地址。
  • 而且控制台也會打印相關信息,說明第二次也成功地訪問服務器端的Servlet

2.6. 生成響應體內容

到目前為止,操作的都是Response對象的響應頭信息。而Http協議的響應協議中,除了響應頭信息之外,還有響應體,如何利用Response對象向客戶端發送響應體呢?可以利用Response對象的getWriter()方法或getOutputStream()方法獲取響應輸出流,通過響應輸出流向客戶端進行響應。具體操作步驟如下:

  • 創建一個Servlet用於向客戶端響應內容。
public class ResponseServlet5 extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PrintWriter out = response.getWriter();
        out.println("<HTML>");
        out.println("  <BODY>");
        out.println("    <H1>Servlet生成的HTML頁面.</H1>");
        out.println("  </BODY>");
        out.println("</HTML>");
    }
 public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}
  • Web工程的web.xml文件中注冊Servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <servlet>
    <servlet-name>ResponseServlet5</servlet-name>
    <servlet-class>app.java.response.ResponseServlet5</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>ResponseServlet5</servlet-name>
    <url-pattern>/response5</url-pattern>
  </servlet-mapping>    
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>
  • 打開瀏覽器,在地址欄中輸入http://localhost:8080/response/response5。
  • 發現在響應內容的頁面中,中文顯示為亂碼。利用Response對象響應前,需要設置響應編碼的字符集。
response.setCharacterEncoding("utf-8");

需要注意的是,調用Response對象的setCharacterEncoding()方法設置編碼字符集時,必須要在調用Response對象的getWriter()方法或getOutputStream()方法之前。

  • 重新打開IE瀏覽器,在地址欄中輸入http://localhost:8080/response/response5。
  • 但是,打開火狐瀏覽器,在地址欄中輸入http://localhost:8080/response/response5。

雖然在響應之前設置了響應編碼字符集,但是使用火狐瀏覽器顯示時,依舊是亂碼。

  • 查看火狐瀏覽器的字符集編碼,會發現並不是utf-8編碼。

這說明利用Response對象的setCharacterEncoding()方法雖然可以設置響應體內容的編碼字符集,但並不能通過瀏覽器,瀏覽器顯示默認使用的編碼集還是瀏覽器默認設置的。可以調用Response對象的setContentType()方法設置響應體的文件類型和編碼字符集。

response.setContentType("text/html;charset=utf-8");
  • 重新打開火狐瀏覽器,在地址欄中輸入http://localhost:8080/response/response5亂碼問題解決

需要注意的是,其實在調用Response對象的setContentType()方法時,設置的編碼字符集是覆蓋了Response對象的setCharacterEncoding()方法設置的編碼字符集的。所以,實際上只調用setContentType()方法即可。

利用Response對象的getWriter()方法或getOutputStream()方法向客戶端進行響應的時候,需要注意的問題是:

  • 如果需要手動響應內容的時候,使用getWriter()方法,否則使用getOutputStream()方法。
  • getWriter()方法和getOutputStream()方法之間是互斥的,也就是說只能使用其中一個方法。
  • 使用getWriter()方法或getOutputStream()方法只能向客戶端響應體,而不能修改響應行和響應頭信息。
  • getWriter()方法或getOutputStream()方法進行響應時,Tomcat服務器會自動關閉響應輸出流,不必手動關閉響應輸出流。

2.7. 生成驗證碼圖片

利用Response對象向客戶端進行響應的功能,來完成頁面中驗證碼生成的案例,具體實現步驟如下:

  • 創建一個HTML頁面用於顯示生成的驗證碼。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>identi.html</title>
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="this is my page">
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <img src="/response/response6">
  </body>
</html>
  • 創建一個Servlet用於生成頁面實現的驗證碼。
public class ResponseServlet6 extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        /*
         * 當前Servlet的作用:生成HTML頁面顯示的驗證碼(圖片).
         *  * Java生成圖片內容,使用圖形界面技術的awt、swing包.
         */
        /*
         *  1 在內存中創建圖片
         *   * 創建圖片,需要定義圖片的寬度和高度.
         *   * 利用BufferedImage類來創建圖片.
         *   * new BufferedImage(寬度, 高度, 圖片類型)
         */
        int width = 120;
        int height = 30;
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        /*
         *  2 繪制圖片背景顏色
         *   * 通過創建的圖片對象的getGraphics()方法,獲取畫筆.
         *   * 通過畫筆對象的setColor()方法設置圖片的背景顏色.
         *   * 通過畫筆對象的fillRect()方法設置背景顏色填充的面積.
         */
        Graphics2D graphics2d = (Graphics2D)image.getGraphics();
        graphics2d.setColor(Color.GRAY);
        graphics2d.fillRect(0, 0, width, height);
        /*
         *  3 繪制邊框
         *   * 通過畫筆對象的drawRect()方法繪制邊框的面積.
         */
        graphics2d.setColor(Color.BLACK);
        graphics2d.drawRect(1, 1, width - 1, height - 1);
        /*
         *  4 向圖片中生成顯示的驗證碼內容
         *   * 通過畫筆對象的setFont()方法設置驗證碼內容的字體、大小等.
         *   * word表示生成驗證碼的備選文本內容.
         */
        graphics2d.setColor(Color.RED);
        graphics2d.setFont(new Font("新宋體", Font.BOLD, 24));
        String word = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
        // 這段代碼用於將生成的驗證碼內容寫入到圖片中.
        Random random = new Random();
        int x = 5;
        for (int i = 0; i < 4; i++) {
            // 加入字體旋轉 角度為"-30-30"之間.
            int jiaodu = random.nextInt(60) - 30;
            // 轉換角度為弧度.
            double theta = jiaodu * Math.PI / 180;
            // 生成下標
            int randomIndex = random.nextInt(word.length());
            // 獲取用於驗證碼顯示的字符.
            char c = word.charAt(randomIndex);
            // 將字符寫入圖片.
            graphics2d.rotate(theta, x, 20);
            graphics2d.drawString(c + "", x, 20);
            graphics2d.rotate(-theta, x, 20);
            // 設置下一個字符出現的水平坐標.
            x += 30;
        }
        // 5 繪制干擾線
        graphics2d.setColor(Color.LIGHT_GRAY);
        for (int i = 0; i < 10; i++) {
            int x1 = random.nextInt(width);
            int x2 = random.nextInt(width);
            int y1 = random.nextInt(height);
            int y2 = random.nextInt(height);
            graphics2d.drawLine(x1, y1, x2, y2);
        }
        // 6 釋放內存中的資源
        graphics2d.dispose();
        // 7 將生成的圖片,響應到客戶端瀏覽器
        ImageIO.write(image, "jpg", response.getOutputStream());
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}
  • Web工程的web.xml文件中注冊Servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <servlet>
    <servlet-name>ResponseServlet6</servlet-name>
    <servlet-class>app.java.response.ResponseServlet6</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>ResponseServlet6</servlet-name>
    <url-pattern>/response6</url-pattern>
  </servlet-mapping>    
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>
  • 打開瀏覽器,在地址欄中輸入http://localhost:8080/response/response6

如果驗證碼的內容設置為中文的話,只需要將上述代碼中的word變量的值設置為常用漢字即可。

到目前為止,生成的驗證碼需要每次刷新頁面才能重新生成驗證碼,如何實現鼠標點擊驗證碼圖片改變驗證碼內容呢?具體實現步驟如下:

  • 需要在顯示驗證碼圖片的HTML頁面中,使用JavaScript代碼來實現。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>identi.html</title>
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="this is my page">
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </head>
  <script type="text/javascript">
      function change(){
        document.getElementById("myimg").src = "/response/response6";
    }
  </script>
  <body>
    <img src="/response/response6" id="myimg" onclick="change();" style="cursor: pointer;">
  </body>
</html>

這樣實現之后,實際測試發現驗證碼內容並沒有改變。原因是IE瀏覽器緩存問題,有兩種方式來解決:

  • 利用之前掌握的通過設置響應頭信息中的內容,禁止瀏覽器保存緩存內容。
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", -1);
  • 通過設置img圖片每次請求的URL地址不同解決這個問題。
document.getElementById("myimg").src = "/response/response6?"+new Date().getTime();

3. 掌握Request

3.1. Request概述

RequestWeb應用程序用來封裝向客戶端請求信息的,是Servlet接口的service()方法的一個參數,類型為javax.servlet.http.HttpServletRequest。客戶端每次發送請求時,服務器都會創建一個Request對象,並傳遞給Servlet接口的service()方法,來完成向客戶端的請求工作。

下列是javax.servlet.http. HttpServletRequest常用的API列表:

Method Summary

 String

getContextPath() 
          Returns the portion of the request URI that indicates the context of the request.

 long

getDateHeader(String name) 
          Returns the value of the specified request header as a long value that represents a Date object.

 String

getHeader(String name) 
          Returns the value of the specified request header as a String.

 Enumeration

getHeaderNames() 
          Returns an enumeration of all the header names this request contains.

 Enumeration

getHeaders(String name) 
          Returns all the values of the specified request header as an Enumeration of String objects.

 int

getIntHeader(String name) 
          Returns the value of the specified request header as an int.

 String

getMethod() 
          Returns the name of the HTTP method with which this request was made, for example, GET, POST, or PUT.

 String

getPathInfo() 
          Returns any extra path information associated with the URL the client sent when it made this request.

 String

getQueryString() 
          Returns the query string that is contained in the request URL after the path.

 String

getRequestURI() 
          Returns the part of this request's URL from the protocol name up to the query string in the first line of the HTTP request.

 StringBuffer

getRequestURL() 
          Reconstructs the URL the client used to make the request.

 String

getServletPath() 
          Returns the part of this request's URL that calls the servlet.

下列是javax.servlet.ServletRequest常用的API列表:

Method Summary

 String

getCharacterEncoding() 
          Returns the name of the character encoding used in the body of this request.

 String

getContentType() 
          Returns the MIME type of the body of the request, or null if the type is not known.

 String

getParameter(String name) 
          Returns the value of a request parameter as a String, or null if the parameter does not exist.

 String

getProtocol() 
          Returns the name and version of the protocol the request uses in the form protocol/majorVersion.minorVersion, for example, HTTP/1.1.

 String

getRealPath(String path) 
          Deprecated. As of Version 2.1 of the Java Servlet API, use ServletContext.getRealPath(java.lang.String) instead.

 String

getRemoteAddr() 
          Returns the Internet Protocol (IP) address of the client or last proxy that sent the request.

 String

getRemoteHost() 
          Returns the fully qualified name of the client or the last proxy that sent the request.

 int

getRemotePort() 
          Returns the Internet Protocol (IP) source port of the client or last proxy that sent the request.

 RequestDispatcher

getRequestDispatcher(String path) 
          Returns a RequestDispatcher object that acts as a wrapper for the resource located at the given path.

 String

getServerName() 
          Returns the host name of the server to which the request was sent.

 int

getServerPort() 
          Returns the port number to which the request was sent.

 

3.2. 獲取請求行信息

在學習Http請求協議時,知道Http請求協議包含請求行、請求頭及請求體三個部分。首先我們來討論請求協議中的請求行,請求行包含請求方式、請求鏈接及GET方式的參數和Http請求協議版本。

GET方式的請求行內容:

GET /http/01_get.html?username=zhangsan HTTP/1.1

POST方式的請求行內容

POST /http/02_post.html HTTP/1.1

獲取Http請求協議中請求行的內容,可以通過以下方法:

  • getMethod()方法:獲取Http請求協議的請求方式,例如GETPOST等。
  • getRequestRUI()方法:獲取Http請求協議的資源路徑。
  • getProtocol()方法:獲取Http請求協議的協議版本。
  • getQueryString()方法:獲取Http請求協議GET方式的請求參數。

下面我們通過代碼實現,來驗證一下上述方法的功能:

  • 創建一個Servlet用於打印上述方法。
public class RequestServlet1 extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 獲取請求方式,GET或POST
        System.out.println("請求方式:"+request.getMethod());
        // 獲取請求協議
        System.out.println("請求協議:"+request.getProtocol());
        // 獲取請求路徑URI和URL
        System.out.println("請求路徑URI:"+request.getRequestURI());
        System.out.println("請求路徑URL:"+request.getRequestURL());
        // 獲取請求參數
        System.out.println("請求參數:"+request.getQueryString());
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}
  • Web工程的web.xml文件中注冊Servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <servlet>
    <servlet-name>RequestServlet1</servlet-name>
    <servlet-class>app.java.request.RequestServlet1</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>RequestServlet1</servlet-name>
    <url-pattern>/request1</url-pattern>
  </servlet-mapping>    
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>
  • Web工程發布到Tomcat服務器,並啟動Tomcat服務器。
  • 打開瀏覽器,在地址欄中輸入http://localhost:8080/response/request1。
  • 在地址后增加參數http://localhost:8080/response/request1?username=zhangsan

除上述方法可以獲取請求協議的請求行中信息外,我們還可以通過getRemoteAddr()方法獲取客戶端IP地址和getContextPath()方法獲取Web工程虛擬目錄名稱。

// 獲取客戶端IP地址
System.out.println("客戶端IP地址:"+request.getRemoteAddr());
// 獲取Web工程虛擬目錄名稱
System.out.println("Web工程虛擬目錄名稱:"+request.getContextPath());

3.3. 獲取請求頭信息

下面我們來討論Http請求協議中的請求頭信息,下面是一個Http請求協議的請求頭信息內容:

Accept: application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, */*
Referer: http://localhost:8080/07_http/01_get.html
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; qdesk 2.5.1277.202; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.3)
Accept-Encoding: gzip, deflate
If-Modified-Since: Thu, 11 Sep 2014 02:44:35 GMT
If-None-Match: W/"679-1410403475587"
Host: localhost:8080
Connection: Keep-Alive

我們之前曾利用請求頭信息中的Referer完成了防盜鏈案例,下面我們來回顧一下。

  • 首先我們創建一個網站的主頁面。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>index.html</title>
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="this is my page">
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <a href="refererServlet">特價商品</a>
  </body>
</html>
  • 然后我們完成服務器端Servlet防止盜鏈的邏輯內容。
public class RefererServlet extends HttpServlet {
    // 處理GET方式的請求
    public void doGet(HttpServletRequest request, HttpServletResponse response)  throws ServletException, IOException {
        // 判斷請求中referer是否存在,有效  --- 防止盜鏈
        String referer = request.getHeader("referer");
        if(referer!=null && referer.equals("http://localhost:8080/http/index.html")){
            // 有效
            response.setContentType("text/html;charset=utf-8");
            response.getWriter().println("筆記本1000元");
        }else{
            // 無效
            response.setContentType("text/html;charset=utf-8");
            response.getWriter().println("盜鏈真無恥!");
        }
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}
  • 將工程發布到Tomcat服務器,啟動Tomcat服務器。
  • 打開瀏覽器,輸入http://localhost:8080/http/index.html地址。
  • 在網站主頁面中,點擊“特價商品”鏈接,會返回正確內容。
  • 如果在瀏覽器地址欄中直接輸入http://localhost:8080/http/refererServlet鏈接。
  • 由於直接輸入鏈接地址的請求協議中,沒有referer信息,所以會顯示報錯信息。

在請求頭信息中,除了Referer可以實現防盜鏈,還可以利用User-Agent獲取客戶端瀏覽器相關信息。

  • 創建一個Servlet用於打印User-Agent請求頭信息。
public class RequestServlet2 extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("瀏覽器是:"+request.getHeader("user-agent"));
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}
  • Web工程的web.xml文件中注冊Servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <servlet>
    <servlet-name>RequestServlet2</servlet-name>
    <servlet-class>app.java.request.RequestServlet2</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>RequestServlet2</servlet-name>
    <url-pattern>/request2</url-pattern>
  </servlet-mapping>    
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>
  • Web工程發布到Tomcat服務器,並啟動Tomcat服務器。
  • 打開瀏覽器,在地址欄中輸入http://localhost:8080/response/request2。

我們還可以利用getHeaderNames()方法打印所有請求頭信息。

Enumeration<String> names = request.getHeaderNames();
while (names.hasMoreElements()) {
    String name = (String) names.nextElement();
    System.out.println(name+":"+request.getHeader(name));
}

3.4. 獲取請求參數

由於Http請求協議中的請求方式常用的有兩種GETPOSTGET方式的請求參數在請求行中的資源路徑后面,POST方式的請求參數在請求體中。

在服務器端Servlet獲取請求參數共有以下四種方法:

  • getParameter()方法:獲取指定參數名的參數值(單個值)。
  • getParameterValues()方法:獲取指定參數名的參數值(多個值)。
  • getParameterNames()方法:獲取所有參數的參數名。
  • getParameterMap()方法:獲取參數以name=value形式存儲在Map集合中,將Map集合返回。

首先,我們來討論POST方式的請求參數,在服務器端如何獲取:

  • 創建一個HTML頁面編寫form表單用於提交客戶端數據。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>POST方式請求.html</title>
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="this is my page">
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <h1>最復雜form表單</h1>
    <form method="post" action="/response/request3">
        用戶名 <input type="text" name="username" /><br/>
        密碼 <input type="password" name="password" /><br/>
        性別 <input type="radio" name="gender" value="男" /><input type="radio" name="gender" value="女" /><br/>
        愛好 <input type="checkbox" name="hobby" value="體育" />體育
        <input type="checkbox" name="hobby" value="音樂" />音樂
        <input type="checkbox" name="hobby" value="讀書" />讀書<br/>
        城市 <select name="city">
            <option value="北京">北京</option>
            <option value="上海">上海</option>
            <option value="廣州">廣州</option>
        </select> <br/>
        個人簡介 <textarea rows="5" cols="60" name="introduce"></textarea><br/>
        <input type="submit" value="提交" />
    </form>
  </body>
</html>
  • 創建一個Servlet用於接收客戶端瀏覽器請求參數。
public class RequestServlet3 extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String gender = request.getParameter("gender");
        String hobby = request.getParameter("hobby");
        String city = request.getParameter("city");
        String introduce = request.getParameter("introduce");
        
        System.out.println("姓名:" + username);
        System.out.println("密碼:" + password);
        System.out.println("性別:" + gender);
        System.out.println("愛好:" + hobby);
        System.out.println("城市:" + city);
        System.out.println("個人介紹:" + introduce);
    }
}
  • Web工程的web.xml文件中注冊Servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <servlet>
    <servlet-name>RequestServlet3</servlet-name>
    <servlet-class>app.java.request.RequestServlet3</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>RequestServlet3</servlet-name>
    <url-pattern>/request3</url-pattern>
  </servlet-mapping>    
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>
  • Web工程發布到Tomcat服務器,並啟動Tomcat服務器。
  • 打開瀏覽器,在地址欄中輸入http://localhost:8080/response/request/ POST.html。
  • HTML頁面中,輸入相關信息后,點擊“提交”按鈕。
  • 在控制台打印信息時,發現都是亂碼。POST方式解決亂碼,需要使用setCharacterEncoding()方法。
request.setCharacterEncoding("utf-8");
  • 在查看控制台打印信息,這時的信息已經顯示正常中文了。

獲取頁面請求的數據信息中,“愛好”內容為多選框,但實際上只打印其中一項。需要調用getParameterValues()方法來解決。

String[] hobby = request.getParameterValues("hobby");
System.out.println("愛好:" + Arrays.toString(hobby));

下面,我們來討論一下GET方式的請求參數,在服務器端如何獲取:

  • HTML頁面中表單的Method改為“GET”。
  • 編寫Servlet用於接收客戶端瀏覽器請求參數。
public class RequestServlet3 extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String gender = request.getParameter("gender");
        String hobby = request.getParameter("hobby");
        String city = request.getParameter("city");
        String introduce = request.getParameter("introduce");
        
        System.out.println("姓名:" + username);
        System.out.println("密碼:" + password);
        System.out.println("性別:" + gender);
        System.out.println("愛好:" + hobby);
        System.out.println("城市:" + city);
        System.out.println("個人介紹:" + introduce);
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
    }
}
  • 打開瀏覽器,在地址欄中輸入http://localhost:8080/response/request/ POST.html。
  • 在控制台打印信息時,發現都是亂碼。

POST方式解決亂碼,有兩種方式:

  • 修改Tomcat服務器安裝目錄的conf目錄中的server.xml文件。
<Connector port="8080" protocol="HTTP/1.1"
         connectionTimeout="20000"
         redirectPort="8443"
RUIEncoding="utf-8" />
  • 通過代碼進行逆向解碼(以用戶名為例)。
username = URLEncoder.encode(username, "ISO-8859-1");
username = URLDecoder.decode(username, "utf-8");

或者也可以利用下面這種方式來解決逆向解碼。

gender = new String(gender.getBytes("ISO-8859-1"), "utf-8");

當然,獲取到客戶端瀏覽器發送的請求參數內容后,還可以完成非空的驗證功能。

if (username != null && username.length() > 0) {
    System.out.println("username 有效.");
}

3.5. 請求轉發數據

之前我們完成了重定向的功能,現在要完成請求轉發的功能。這兩個功能經常會放在一起比較:

  • 重定向:
    • 發生兩次請求,兩次響應。
    • 重定向在客戶端瀏覽器可以查看到。
    • 重定向無法攜帶數據。
    • 重定向中的第二次請求資源路徑來源於客戶端。
  • 請求轉發:
    • 發生一次請求,一次響應。
    • 請求轉發在客戶端瀏覽器無法查看。
    • 請求轉發可以攜帶數據。
    • 請求轉發的第二次請求資源路徑來源於服務器端內部。

下面我們來實現請求轉發的功能,再比較請求轉發與重定向的區別。

  • 創建一個Servlet用於接收客戶端請求。
public class RequestServlet4 extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("這是Servlet4...");
        // 獲取請求轉發對象
        RequestDispatcher dispatcher = request.getRequestDispatcher("/request5");
        // 利用forward()方法進行請求轉發.
        dispatcher.forward(request, response);
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}
  • 再創建一個Servlet用於接收請求轉發的請求。
public class RequestServlet5 extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("這是Servlet5...");
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}
  • Web工程的web.xml文件中注冊Servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <servlet>
    <servlet-name>RequestServlet4</servlet-name>
    <servlet-class>app.java.request.RequestServlet4</servlet-class>
  </servlet>
  <servlet>
    <servlet-name>RequestServlet5</servlet-name>
    <servlet-class>app.java.request.RequestServlet5</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>RequestServlet4</servlet-name>
<url-pattern>/request4</url-pattern>
  </servlet-mapping>    
  <servlet-mapping>
    <servlet-name>RequestServlet5</servlet-name>
<url-pattern>/request5</url-pattern>
  </servlet-mapping>    
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>
  • 打開瀏覽器,在地址欄輸入http://localhost:8080/response/request4
  • 通過Request對象的setAttribute()方法和getAttribute()方法來攜帶數據。

RequestServlet4

// 向Request對象中,存儲數據內容.
request.setAttribute("name", "longestory");

RequestServlet5

String name = (String)request.getAttribute("name");
System.out.println("獲取的name為:"+name);
  • 打開瀏覽器,在地址欄輸入http://localhost:8080/response/request4
  • 而從客戶端瀏覽器通過HttpWatch工具查看請求時,只發生了一次請求。


免責聲明!

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



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