1. 程式人生 > 其它 >JavaWeb筆記Day7------Servlet,Request和Response

JavaWeb筆記Day7------Servlet,Request和Response

web相關概念回顧

軟體架構

  1. C/S:客戶端/伺服器端
  2. B/S:瀏覽器/伺服器端

資源分類

  1. 靜態資源:所有使用者訪問後,得到的結果都是一樣的,稱為靜態資源,靜態資源可以直接被瀏覽器解析
    • 如:HTML,css,JavaScript
  2. 動態資源:每個使用者訪問相同資源後,得到的結果可能不一樣。稱為動態資源。動態資源被訪問後,需要先轉換為靜態資源,再返回給瀏覽器
    • 如servlet/jsp,php,asp...

網路通訊三要素

  1. IP:電子裝置(計算機)在網路中的唯一標識
  2. 埠:應用程式在計算機中的唯一標識 0~65536
  3. 傳輸協議:規定了資料傳輸的規則
    1. 基礎協議:
      1. tcp:安全協議,三次握手。速度稍慢
      2. udp:不安全協議。速度快

web伺服器軟體

  • 伺服器:安裝了伺服器軟體的計算機

  • 伺服器軟體:接收使用者的請求,處理請求,做出響應

  • web伺服器軟體:接收使用者的請求,處理請求,做出響應

    • 在web伺服器軟體中,可以部署web專案,讓使用者通過瀏覽器來訪問這些專案
    • web容器
  • 常見的Java相關的web伺服器軟體

    • webLogic:oracle公司,大型的JavaEE伺服器,支援所有的JavaEE規範,收費的。
    • webSphere:IBM公司,大型的JavaEE伺服器,支援所有的JavaEE規範,收費的。
    • JBOSS:JBOSS公司的,大型的JavaEE伺服器,支援所有的JavaEE規範,收費的。
    • Tomcat:Apache基金組織,中小型的JavaEE伺服器,僅僅支援少量的JavaEE規範servlet/jsp。開源的,免費的。
  • JavaEE:Java語言在企業級開發中使用的技術規範的總和,一共規定了13項大的規範

Tomcat

概念

web伺服器軟體

使用

  1. 下載:http://tomcat.apache.org/

  2. 安裝:解壓壓縮包即可。

    • 注意:安裝目錄建議不要有中文和空格
  3. 解除安裝:刪除目錄就行了

  4. 啟動:

    • bin/startup.bat 雙擊執行即可

    • 訪問:瀏覽器輸入:

    • 可能遇到的問題

      1. 黑視窗一閃而過:

        • 原因:沒有正確配置JAVA_HOME環境變數
        • 解決方案:正確配置JAVA_HOME環境變數
      2. 啟動報錯:

        1. 暴力:找到佔用的埠號,並且找到對應的程序,殺死該程序

          netstat -ano|findstr "埠號"
          taskkill /f /t /im PID號
          
        2. 溫柔:修改自身的埠號(conf/server.xml)

          <Connector port="8888" protocol="HTTP/1.1"
          connectionTimeout="20000"
                     redirectPort="8445" />
          
        3. 一般會將tomcat的預設埠號修改為80。80埠號是http協議的預設埠號

          • 好處:在訪問時就不用輸入埠號
  5. 關閉

    • 正常關閉:
      • bin/shutdown.bat
      • ctrl+c
    • 強制關閉:
      • 點選啟動視窗的x
  6. 配置

    • 部署專案的方式:
      1. 直接將專案放到webapps目錄下即可

        • /hello:專案的訪問路徑-->虛擬路徑
        • 簡化部署:將專案打成一個war包,再將war包放置到webapps目錄下
          • war包會自動解壓縮
      2. 配置conf/server.xml檔案

        在<host>標籤體中配置

        <Context docBase="D:\hello path="/hehe" />

        • docBase:專案存放的路徑
        • path:虛擬目錄
      3. 在conf\Catalina\localhost建立任意名稱的xml檔案。在檔案中編寫
        <Context docBase="D:\hello />

        • 虛擬目錄:xml的檔名稱

目錄結構

Java動態專案的目錄結構

---- 專案根目錄

​ ---- WEB-INF目錄:

​ ----web.xml:web專案的核心配置檔案

​ ----classes目錄:放置位元組碼檔案的目錄

​ ----lib目錄:放置依賴的jar包

Servlet:server applet

概念

執行在伺服器端的小程式

  • Servlet就是一個介面,定義了Java類被瀏覽器訪問(Tomcat識別)到的規則。
  • 將來我們自定義一個類,實現Servlet介面,複寫方法

步驟

  1. 建立JavaEE專案

  2. 定義一個類,實現Servlet介面

    package web.servlet;
    
    import javax.servlet.*;
    import java.io.IOException;
    
    public class ServletDemo1 implements Servlet {
    
        @Override
        public void init(ServletConfig servletConfig) throws ServletException {
    
        }
    
        @Override
        public ServletConfig getServletConfig() {
            return null;
        }
    
        //提供服務的方法
        @Override
        public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
            System.out.println("Hello Servlet!");
        }
    
        @Override
        public String getServletInfo() {
            return null;
        }
    
        @Override
        public void destroy() {
    
        }
    }
    
  3. 實現介面中的抽象方法

  4. 配置Servlet

    • 在web.xml中配置

          <!--配置Servlet-->
          <servlet>
              <servlet-name>helloServlet</servlet-name>
              <!--全類名-->
              <servlet-class>web.servlet.ServletDemo1</servlet-class>
          </servlet>
      
          <servlet-mapping>
              <servlet-name>helloServlet</servlet-name>
              <!--訪問路徑-->
              <url-pattern>/hello</url-pattern>
          </servlet-mapping>
      

執行原理

  1. 當伺服器接收到客戶端瀏覽器的請求後,會解析請求URL路徑,獲取訪問的Servlet的資源路徑。
  2. 查詢web.xml檔案,是否有對應的<url-pattern>標籤體內容。
  3. 如果有,則在找到對應的<servlet-class>全類名
  4. Tomcat會將位元組碼檔案載入進記憶體,並且建立其物件
  5. 呼叫其方法

Servlet中的生命週期

  1. 被建立:執行init方法(一般用於載入資源),只執行一次

    • Servlet什麼時候被建立

      • 預設情況下,第一次被訪問時,Servlet被建立

      • 可以配置執行Servlet的建立時機

        • 在servlet標籤下配置

          1. 第一次被訪問時,建立
            <load-on-startup>的值為負數
          2. 在伺服器啟動時,建立
            <load-on-startup>的值為0或正整數
          <load-on-startup>-1</load-on-startup>
          
    • Servlet的init方法,只執行一次,說明一個servlet在記憶體中只存在一個物件,Servlet是單例的

      • 多個使用者同時訪問時,可能存線上程安全問題
      • 解決:儘量不要在Servlet中定義成員變數。即使定義了成員變數,也不要對其修改值
  2. 提供服務:執行service方法,執行多次

    • 每次訪問Servlet時,Servlet方法都會被呼叫一次
  3. 被銷燬:執行destroy方法,只執行一次

    • Servlet被銷燬時執行。伺服器關閉時,Servlet被銷燬
    • 只有伺服器正常關閉時,才會執行destroy方法
    • destroy方法在Servlet被銷燬之前執行,一般用於釋放資源
package web.servlet;

import javax.servlet.*;
import java.io.IOException;

public class ServletDemo2 implements Servlet {
    /**
     * 初始化方法
     * 在Servlet被建立時,執行。只會執行一次
     * @param servletConfig
     * @throws ServletException
     */
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("ServletDemo2 init");
    }

    /**
     * 獲取ServletConfig物件
     * ServletConfig物件是Servlet的配置物件
     * @return
     */
    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    /**
     * 提供服務方法
     * 每一次Servlet被訪問時,執行。執行多次
     * @param servletRequest
     * @param servletResponse
     * @throws ServletException
     * @throws IOException
     */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("ServletDemo2 service");
    }

    /**
     * 獲取Servlet的一些資訊,版本,作者等等...
     * @return
     */
    @Override
    public String getServletInfo() {
        return null;
    }

    /**
     * 銷燬方法
     * 在Servlet被銷燬時(伺服器正常關閉時),執行。執行一次
     */
    @Override
    public void destroy() {
        System.out.println("ServletDemo2 destroy");
    }
}

Servlet3.0

好處

支援註解配置。可以不需要web.xml了

步驟

  1. 建立JavaEE專案,選擇Servlet的版本3.0以上,可以不建立web.xml

  2. 定義一個類,實現Servlet介面

  3. 複寫方法

  4. 在類上使用@WebServlet註解,進行配置

    @WebServlet("資源路徑")
    @WebServlet(urlPatterns = "資源路徑")
    

IDEA與Tomcat的相關配置

Servlet的體系結構

  • Servlet------介面
    |
    GenericServlet------抽象類
    |
    HttpServlet-----抽象類

  • GenericServlet:將Servlet介面中其他的方法做了預設空實現

    • 將來定義Servlet類可以繼承GenericServlet,實現service()方法即可
  • HttpServlet:對http協議的一種封裝,簡化操作

    1. 定義類繼承HTTPServlet

    2. 複寫doGet/doPost方法

    • 原始碼如下:

       /**
       * javax.servlet.http.HttpServlet protected void service(@NotNull HttpServletRequest req,
       *                        HttpServletResponse resp) throws ServletException, IOException
       * 從公共service方法接收標準 HTTP 請求,並將它們分派到此類中定義的do Method方法。此方法是javax.servlet.Servlet.service方法的 HTTP    特定版本。無需重寫此方法。
       *
       * 參形:
       *      req – HttpServletRequest物件,其中包含客戶端對 servlet 發出的請求
       *     resp – HttpServletResponse物件,其中包含 servlet 返回給客戶端的響應
       * 丟擲:
       *      IOException – 如果在 servlet 處理 HTTP 請求時發生輸入或輸出錯誤
       *      ServletException – 如果無法處理 HTTP 請求
       * 請參閱:
       *      javax.servlet.Servlet.service
       * 推斷註解:
       *      @org.jetbrains.annotations.NotNull
       * apache-tomcat-9.0.8-src
       */
          protected void service(HttpServletRequest req, HttpServletResponse resp)
              throws ServletException, IOException {
      
              String method = req.getMethod();
      
              if (method.equals(METHOD_GET)) {
                  long lastModified = getLastModified(req);
                  if (lastModified == -1) {
                      // servlet doesn't support if-modified-since, no reason
                      // to go through further expensive logic
                      doGet(req, resp);
                  } else {
                      long ifModifiedSince;
                      try {
                          ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                      } catch (IllegalArgumentException iae) {
                          // Invalid date header - proceed as if none was set
                          ifModifiedSince = -1;
                      }
                      if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                          // If the servlet mod time is later, call doGet()
                          // Round down to the nearest second for a proper compare
                          // A ifModifiedSince of -1 will always be less
                          maybeSetLastModified(resp, lastModified);
                          doGet(req, resp);
                      } else {
                          resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                      }
                  }
      
              } else if (method.equals(METHOD_HEAD)) {
                  long lastModified = getLastModified(req);
                  maybeSetLastModified(resp, lastModified);
                  doHead(req, resp);
      
              } else if (method.equals(METHOD_POST)) {
                  doPost(req, resp);
      
              } else if (method.equals(METHOD_PUT)) {
                  doPut(req, resp);
      
              } else if (method.equals(METHOD_DELETE)) {
                  doDelete(req, resp);
      
              } else if (method.equals(METHOD_OPTIONS)) {
                  doOptions(req,resp);
      
              } else if (method.equals(METHOD_TRACE)) {
                  doTrace(req,resp);
      
              } else {
                  //
                  // Note that this means NO servlet supports whatever
                  // method was requested, anywhere on this server.
                  //
      
                  String errMsg = lStrings.getString("http.method_not_implemented");
                  Object[] errArgs = new Object[1];
                  errArgs[0] = method;
                  errMsg = MessageFormat.format(errMsg, errArgs);
      
                  resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
              }
          }
      

Servlet相關配置

  1. urlpartten:Servlet訪問路徑
    1. 一個Servlet可以定義多個訪問路徑:@WebServlet({"/d4","/dd4","/ddd4"})
    2. 路徑定義規則:
      1. /xxx
      2. /xxx/xxx:多層路徑,目錄結構
      3. *.do

HTTP協議

概念

Hyper Text Transfer Protocal 超文字傳輸協議

  • 傳輸協議:定義了,客戶端和服務端通訊時,傳送資料的格式

  • 特點:

    1. 基於TCP/IP的高階協議
    2. 預設埠號:80
    3. 基於請求響應模型的:一次請求對應一次響應
    4. 無狀態的:每次請求之間相互獨立,不能互動資料
  • 歷史版本:

    • 1.0:每一次請求響應都會建立新的連線

    • 1.1:複用連線

請求訊息

概念

客戶端傳送給伺服器端的資料

資料格式

  1. 請求行

    請求方式 請求url 請求協議/版本
    GET /login.html HTTP/1.1
    • 請求方式:
      • http協議有7種請求方式,常用的有兩種
        • GET:
          1. 請求引數在請求行中,在url後。
          2. 請求的url長度有限制的
          3. 不太安全
        • POST:
          1. 請求引數在請求體中
          2. 請求的url長度沒有限制的
          3. 相對安全
  2. 請求頭:客戶端瀏覽器告訴伺服器一些資訊

    請求頭名稱:請求頭值

    • 常見的請求頭:
      1. User-Agent:瀏覽器告訴伺服器,我訪問你使用的瀏覽器版本資訊

        • 可以在伺服器端獲取該頭的資訊,解決瀏覽器的相容性問題
      2. Referer:http://localhost/login.html

        • 告訴伺服器,我(當前請求)從哪裡來?
          1. 防盜鏈:
          2. 統計工作:
        • 防盜鏈:
  3. 請求空行

    空行,就是用於分割POST請求的請求頭,和請求體的。

  4. 請求體(正文)

    • 封裝POST請求訊息的請求引數的

響應訊息

  • 概念:伺服器端傳送給客戶端的資料
  • 資料格式:
    1. 響應行

      1. 組成:協議/版本 響應狀態碼 狀態碼描述
      2. 響應狀態碼:伺服器告訴客戶端瀏覽器本次請求和響應的一個狀態
        1. 狀態碼都是三位數字

        2. 分類:

          • 1xx:伺服器接收客戶端訊息,但沒有接收完成,等待一段時間後,傳送1xx狀態碼

          • 2xx:成功。代表:200

          • 3xx:重定向。代表:302(重定向),304(訪問快取)

          • 4xx:客戶端錯誤。代表:404(請求路徑沒有對應的資源)405(請求方式沒有對應的doXxx方法)

          • 5xx:伺服器端錯誤。代表:500(伺服器內部出現異常)

    2. 響應頭:

      1. 格式:頭名稱:值
      2. 常見的響應頭:
        1. Content-Type:伺服器告訴客戶端本次響應體資料格式以及編碼格式
        2. Content-disposition:伺服器告訴客戶端以什麼格式開啟響應體資料
          • 值:
            • in-line:預設值,在當前頁面內開啟
            • attachment;filename=xxx:以附件形式開啟響應體。檔案下載
    3. 響應空行

    4. 響應體:傳輸的資料

Request

request物件和response物件的原理

  1. request和response物件是由伺服器建立的。我們來使用它們
  2. request物件是來獲取請求訊息,response物件是來設定響應訊息

request物件繼承體系結構

request功能

獲取請求訊息

獲取請求行資料
  • GET /day14/demo1?name=zhangsan HTTP/1.1
  • 方法:
    1. 獲取請求方式:GET

      String getMethod();
      
    2. 獲取虛擬目錄:/day14

      String getContextPath();
      
    3. 獲取Servlet路徑:demo1

      String getServletPath();
      
    4. 獲取get方式請求引數:name=zhangsan

      String getQueryString();
      
    5. 獲取請求URI:/day14/demo1

      //day14/demo1
      String getRequestURI();
      //http://localhost/day14/demo1
      StringBuffer getRequestURL();
      
    6. 獲取協議及版本:HTTP/1.1

      String getProtocol();
      
      
    7. 獲取客戶機的IP地址:

      String getRemoteAddr();
      
獲取請求頭資料
  • 方法:

    //通過請求頭的名稱獲取請求頭的值
    String getHeader(String name);
    
    //獲取所有的請求頭名稱
    Enumeration<String> getHeaderNames();
    
獲取請求體資料
  • 請求體:只有POST請求方式,才有請求體,在請求體中封裝了POST請求的請求引數

  • 步驟:

    1. 獲取流物件

      //獲取字元輸入流,只能操作字元資料
      BufferedReader getReader();
      
      //獲取位元組輸入流,可以操作所有型別資料
      ServletInputStream getInputStream();
      
    2. 再從流物件中拿資料

其他功能

獲取請求引數通用方式

不論get還是post請求方式都可以使用下列方法來獲取請求引數

//根據引數名稱獲取引數值 username=zs&password=123
String getParameter(String name);

//根據引數名稱獲取引數值的陣列
String[] getParameterValues(String name);

//獲取所有請求的引數名稱
Enumration<String> getParameterNames();

//獲取所有引數的Map集合
Map<String,String[]>getParameterMap();
  • 中文亂碼問題:

    • get方式:Tomcat8已經將get方式亂碼問題解決了

    • post方式:會亂碼

      • 解決:在獲取引數前,設定請求request的編碼

        request.setCharacterEncoding("utf-8");
        
請求轉發
  • 概念:一種在伺服器內部的資源跳轉方式

  • 步驟:

    1. 通過request物件獲取請求轉發器物件:

      RequestDispatcher getRequestDispatcher(String path);
      
    2. 使用RequestDispatcher物件來進行轉發

      forward(ServletRequest request,Servlet Response response);
      
  • 特點:

    1. 瀏覽器位址列路徑不發生變化
    2. 只能轉發到當前伺服器內部資源中
    3. 轉發是一次請求
共享資料
  • 域物件:一個有作用範圍的物件,可以在範圍內共享資料

  • request域:代表一次請求的範圍,一般用於請求轉發的多個資源中共享資料

  • 方法:

//儲存資料
void setAttribute(String name,Object obj);
//通過鍵獲取值
Object getAttitude(String name);
//通過鍵移除鍵值對
void removeAttribute(String name);
獲取ServletContext
ServletContext getServletContext();

案例:使用者登入

需求

  1. 編寫login.html登入頁面

    username & password 兩個輸入框

  2. 使用Druid資料庫連線池技術,操作mysql,day14資料庫中user表

  3. 使用JdbcTemplate技術封裝JDBC

  4. 登入成功跳轉到SuccessServlet展示:登入成功!使用者名稱,歡迎您

  5. 登入失敗跳轉到FailServlet展示:登入失敗,使用者名稱或密碼錯誤

分析

步驟

  1. 建立專案,匯入html頁面,配置檔案,jar包

  2. 建立資料庫環境

    CREATE DATABASE Login;
    USE Login;
    CREATE TABLE USER(
               id INTEGER PRIMARY KEY AUTO_INCREMENT,
               username VARCHAR(32) UNIQUE NOT NULL,
               PASSWORD VARCHAR(32) NOT NULL
    );
    INSERT user VALUES('1','admin','123456');
    
  3. 建立包和User類

    package web.test.domain;
    
    /**
     * 使用者的實體類
     */
    public class User {
        private int id;
        private String username;
        private String password;
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    }
    
  4. 建立包和UserDao類

    package web.test.dao;
    
    import org.springframework.dao.DataAccessException;
    import org.springframework.jdbc.core.BeanPropertyRowMapper;
    import org.springframework.jdbc.core.JdbcTemplate;
    import web.test.domain.User;
    import web.test.util.JDBCUtils;
    
    /**
     * 操作資料庫中User表的類
     */
    public class UserDao {
    
        //宣告JDBCTemplate物件共用
        private JdbcTemplate template=new JdbcTemplate(JDBCUtils.getDataSource());
    
    
        /**
         * 登入方法
         * @param loginUser 只有使用者名稱和密碼
         * @return user包含使用者全部資料
         */
        public User login(User loginUser){
            try {
                //1.編寫sql
                String sql="SELECT *FROM user WHERE username=? AND PASSWORD=?";
                //呼叫query方法
                User user=template.queryForObject(sql,
                        new BeanPropertyRowMapper<>(User.class),
                        loginUser.getUsername(),loginUser.getPassword());
                return user;
            } catch (DataAccessException e) {
                e.printStackTrace();//記錄日誌
                return null;
            }
        }
    }
    
    
    
  5. 編寫LoginServlet類

    package web.test.web.servlet;
    
    import web.test.dao.UserDao;
    import web.test.domain.User;
    
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.annotation.*;
    import java.io.IOException;
    
    @WebServlet(name = "LoginServlet", value = "/LoginServlet")
    public class LoginServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //1.設定編碼
            request.setCharacterEncoding("utf-8");
            //2.獲取請求引數
            String username = request.getParameter("username");
            String password = request.getParameter("password");
            //3.封裝user物件
            User loginUser = new User();
            loginUser.setUsername(username);
            loginUser.setPassword(password);
    
            //4.呼叫UserDao的login方法
            UserDao dao=new UserDao();
            User user=dao.login(loginUser);
    
            //5.判斷user
            if (user==null){
                //登入失敗
                request.getRequestDispatcher("/FailServlet").forward(request,response);
            }else {
                //登入成功
                //儲存資料
                request.setAttribute("user",user);
                //轉發
                request.getRequestDispatcher("/SuccessServlet").forward(request,response);
            }
        }
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doGet(request,response);
        }
    }
    
  6. 編寫FailServlet和SuccessServlet類

  7. login.html中form表單的寫法

    • 虛擬目錄+Servlet的資源路徑

BeanUtils

概念

  • 成員變數
  • 屬性:setter和getter方法擷取後的產物
    • 例如:getUsername()---->Username---->username

用途

工具類---簡化資料封裝,用於封裝JavaBean。

JavaBean------標準的Java類

  1. 要求:
    1. 類必須被public修飾
    2. 必須提供空參構造器
    3. 成員變數必須使用private
    4. 提供公共的setter和getter方法
  2. 功能:封裝資料

方法

setProperty();
getProperty();
//將map集合的鍵值對資訊,封裝到對應的JavaBean物件中
populate();

案例

//2. 獲取所有請求引數
Map<String, String[]>map=request.getParameterMap();
//3.建立User物件
User loginUser=new User();
//3.1使用BeanUtils封裝
try {
    BeanUtils.populate(loginUser,map);
} catch (IllegalAccessException e) {
    e.printStackTrace();
} catch (InvocationTargetException e) {
    e.printStackTrace();
}

Respouse物件

功能

設定響應訊息

設定響應行

  1. 格式:HTTP/1.1 200 ok

  2. 設定狀態碼

    setStatus(int sc);
    

設定響應頭

setHeader(String name,String value);

設定響應體

使用步驟:

  1. 獲取輸出流

    //字元輸出流
    PrintWriter getWriter();
    //位元組輸出流
    ServletOutputStream getOutputStream();
    
  2. 使用輸出流,將資料輸出到客戶端瀏覽器

案例

完成重定向

  • 重定向:資源跳轉的方式

  • 程式碼:

    package web.response;
    
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.annotation.*;
    import java.io.IOException;
    
    @WebServlet(name = "responseDemo1", value = "/responseDemo1")
    public class responseDemo1 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request,response);
        }
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("demo1....");
    
            //訪問/responseDemo1,會自動跳轉到/responseDemo2資源
    //        //1. 設定狀態碼為302
    //        response.setStatus(302);
    //        //2. 設定響應頭location
    //        response.setHeader("location","/StudyJavaWeb01/responseDemo2");
    
            //簡單的重定向方法
            //動態獲取虛擬目錄
            String contextPath=request.getContextPath();
            response.sendRedirect(contextPath+"/responseDemo2");
        }
    }
    
  • 重定向的特點:redirect

    1. 位址列發生變化
    2. 重定向可以訪問其他站點(伺服器)的資源
    3. 重定向是兩次請求。不能使用request物件來共享資料
  • 轉發的特點:forward

    1. 轉發位址列路徑不變
    2. 轉發只能訪問當前伺服器下的資源
    3. 轉發是一次請求。可以使用request物件來共享資料
  • 路徑的寫法:

    1. 路徑分類

      1. 相對路徑:通過相對路徑不可以確定唯一資源

        • 如:./index.html
        • 不以/開頭,以.開頭路徑
        • 規則:找到當前資源和目標資源之間的相對位置關係
          • ./:當期資源
          • ../:後退一級目錄
      2. 絕對路徑:通過絕對路徑可以確定唯一資源

        • 如:http://localhost/response/demo1 /response/demo1

        • 以/開頭的路徑

        • 規則:判斷定義的路徑是給誰用的?判斷請求將來從哪兒發出

          • 給客戶端瀏覽器使用:需要加虛擬目錄(專案的訪問路徑)

            • 建議虛擬目錄動態獲取

              String contextPath=request.getContextPath();
              
            • <a>,<form>重定向。。。

          • 給伺服器使用:不需要加虛擬目錄

            • 轉發路徑

伺服器輸出字元資料到瀏覽器

  • 步驟:

    1. 獲取字元輸出流

    2. 輸出資料

      //簡單的形式,設定編碼,在獲取流之前設定
      response.setContentType("text/html;charset=utf-8");
      
  • 注意:

    亂碼問題:

    1. PrintWriter pw=response.getWriter();獲取的流的預設編碼是ISO-8859-1
    2. 設定該流的預設編碼
    3. 告訴瀏覽器響應體使用的編碼
  • 程式碼:

    package web.response;
    
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.annotation.*;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    @WebServlet(name = "responseDemo3", value = "/responseDemo3")
    public class responseDemo3 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //        獲取流物件之前,設定流的預設編碼
    //        response.setCharacterEncoding("utf-8");
            //告訴瀏覽器,伺服器傳送的訊息體資料的編碼,建議瀏覽器使用該編碼解碼
    //        response.setHeader("content-type","text/html;charset=utf-8");
    
            //簡單的形式,設定編碼,在獲取流之前設定
            response.setContentType("text/html;charset=utf-8");
    //        1. 獲取字元輸出流
            PrintWriter pw=response.getWriter();
    //        2. 輸出資料
            pw.write("<h1>Hello response你好<h1>");
        }
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doGet(request,response);
        }
    }
    

伺服器輸出位元組資料到瀏覽器

  • 步驟:
    1. 獲取字元輸出流

    2. 輸出資料

驗證碼

  1. 本質:圖片

  2. 目的:防止惡意表單註冊

  3. 程式碼

    • 生成驗證碼

      package web.response;
      
      import javax.imageio.ImageIO;
      import javax.servlet.*;
      import javax.servlet.http.*;
      import javax.servlet.annotation.*;
      import java.awt.*;
      import java.awt.image.BufferedImage;
      import java.io.IOException;
      import java.util.Random;
      
      @WebServlet(name = "CheckCodeServlet", value = "/CheckCodeServlet")
      public class CheckCodeServlet extends HttpServlet {
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              this.doPost(request,response);
          }
      
          @Override
          protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              int width = 100;
              int height =50;
              //1. 建立物件,在記憶體中畫圖(驗證碼圖片)
              BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
      
              //2.美化圖片
              //2.1 填充背景色
              Graphics g=image.getGraphics();//畫筆物件
              g.setColor(Color.YELLOW);//填充顏色
              g.fillRect(0,0,width,height);//填充矩形
      
              //2.2畫邊框
              g.setColor(Color.BLUE);//設定畫筆顏色
              g.drawRect(0,0,width-1,height-1);//畫矩形
      
              String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
      
              //生成隨機角標
              Random random = new Random();
              for (int i = 1; i <= 4; i++) {
                  int index = random.nextInt(str.length());
                  //獲取字元
                  char ch=str.charAt(index);
                  //2.3 寫驗證碼
                  g.drawString(ch+"",width/5*i,height/2);
              }
      
              //2.4 畫干擾線
              g.setColor(Color.RED);
      
              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);
      
                  g.drawLine(x1,y1,x2,y2);
              }
      
              //3.將圖片輸出到頁面展示
              ImageIO.write(image, "jpg", response.getOutputStream());
          }
      }
      
    • 繫結事件

      <!DOCTYPE html>
      <html lang="en">
      <head>
          
          <title>Title</title>
      </head>
      
      <script>
          /**
           * 分析:
           *      點選超連結或者圖片,需要換一張
           *      1.給超連結或者圖片繫結單擊事件
           *
           *      2.重新設定圖片的src屬性值
           */
      
          window.onload = function(){
              //1.獲取圖片物件
              var img = document.getElementById("checkCode");
              //2.繫結單擊事件
              img.onclick=function(){
                  //加時間戳
                  var data = new Date().getTime();
      
                  img.src = "/StudyJavaWeb01/CheckCodeServlet?a="+data;
              }
      
              var change=document.getElementById("change");
              change.onclick=function(){
                  var data = new Date().getTime();
                  img.src = "/StudyJavaWeb01/CheckCodeServlet?a="+data;
              }
      
      
          }
      </script>
      
      <body>
      <img src="/StudyJavaWeb01/CheckCodeServlet" alt="" id="checkCode">
      <a id="change" href="javascript:void (0)">看不清?換一張</a>
      </body>
      </html>
      

ServletContext物件

概念

代表整個web應用,可以和程式的容器(伺服器)來通訊

獲取

  1. 通過request物件獲取

功能

  1. 獲取MIME型別:

    • MIME型別:在網際網路通訊過程中定義的一種檔案資料型別

      • 格式:大型別/小型別 text/html image/jepg
    • 獲取:

      String getMimeType(String file);
      
  2. 域物件:共享資料

    setAttribute(String name,Object value);
    getAttribute(String name);
    removeAttribute(String name);
    

    ServletContext物件範圍:所有使用者請求的資料

  3. 獲取檔案的真實(伺服器)路徑

    方法:

    String getRealPath(String path)
    
    

案例----檔案下載

需求

  1. 頁面顯示超連結
  2. 點選超連結後彈出下載提示框
  3. 完成圖片檔案下載

分析

  1. 超連結指向的資源如果能夠被瀏覽器解析,則在瀏覽器中展示,如果不能解析,則彈出下載提示框。不滿足需求
  2. 任何資源都必須彈出下載提示框
  3. 任何響應頭設定資源的開啟方式:content-disposition:attachment;filename=xxx

步驟

  1. 定義頁面,編輯超連結href屬性,指向Servlet,傳遞資源名稱filename
  2. 定義Servlet
    1. 獲取檔名稱
    2. 使用位元組輸入流載入進記憶體
    3. 指定response的響應頭:content-disposition:attachment;filename=xxx
    4. 將資料寫出到response輸出流

中文檔名稱問題

  • 解決思路
    1. 獲取客戶端使用的瀏覽器版本資訊
    2. 根據不同的版本資訊,設定filename的編碼方式不同

程式碼

主類

package web.servletcontext;

import web.utils.DownLoadUtils;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.FileInputStream;
import java.io.IOException;

@WebServlet(name = "DownloadServlet", value = "/DownloadServlet")
public class DownloadServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.獲取請求引數,檔名稱
        String fileName = request.getParameter("filename");
        //2.使用位元組輸入流載入進記憶體
        //2.1找到檔案伺服器路徑
        ServletContext ServletContext = this.getServletContext();
        String realPath = ServletContext.getRealPath("/img/" + fileName);
        //2.2用位元組流關聯
        FileInputStream fileInputStream = new FileInputStream(realPath);

        //3.設定response響應頭
        //3.1設定響應頭型別:content-type
        String mimeType = ServletContext.getMimeType(realPath);
        response.setHeader("content-type", mimeType);//獲取檔案的mime型別
        //3.2設定響應頭開啟方式:content-disposition

        //解決中文檔名問題
        //1.獲取user-agent請求頭
        String userAgent = request.getHeader("user-agent");
        //2.使用工具類編碼檔名
        fileName=DownLoadUtils.getFileName(userAgent, fileName);

        response.setHeader("content-disposition", "attachment;filename=" + fileName);



        //4.將輸入流的資料寫到輸出流中
        ServletOutputStream outputStream = response.getOutputStream();
        byte[] bytes = new byte[1024*8];
        int len = 0;
        while ((len = fileInputStream.read(bytes)) != -1) {
            outputStream.write(bytes, 0, len);
        }

        fileInputStream.close();
    }
}

下載頁面

<!DOCTYPE html>
<html lang="en">
<head>
    
    <title>Title</title>
</head>
<body>
<a href="/StudyJavaWeb01/DownloadServlet?filename=圖片20220120085920.jpg">圖片</a>
</body>
</html>

工具類

package web.utils;

import java.util.Base64;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;




public class DownLoadUtils {

    public static String getFileName(String agent, String filename) throws UnsupportedEncodingException {
        if (agent.contains("MSIE")) {
            // IE瀏覽器
            filename = URLEncoder.encode(filename, "utf-8");
            filename = filename.replace("+", " ");
        } else if (agent.contains("Firefox")) {
            // 火狐瀏覽器
            Base64.Encoder encoder = Base64.getEncoder();
            filename = "=?utf-8?B?" + encoder.encodeToString(filename.getBytes("utf-8")) + "?=";
        } else {
            // 其它瀏覽器
            filename = URLEncoder.encode(filename, "utf-8");
        }
        return filename;
    }
}