1. 程式人生 > >javaweb之Servlet,http協議以及請求轉發和重定向

javaweb之Servlet,http協議以及請求轉發和重定向

本文是作者原創,版權歸作者所有.若要轉載,請註明出處.

一直用的框架開發,快連Servlet都忘了,此文旨在幫自己和大家回憶一下Servlet主要知識點.話不多說開始吧

 

用idea構建Servlet專案

 

專案結構如下

 

 

 什麼是 Servlet

  • 1、Servlet 是 JavaEE 規範之一。規範就是介面
  • 2、Servlet 就 JavaWeb 三大元件之一。三大元件分別是:Servlet 程式、Filter 過濾器、Listener 監聽器。
  • 3、Servlet 是執行在伺服器上的一個 java 小程式,它可以接收客戶端傳送過來的請求,並響應資料給客戶端。

實現第一個Servlet 程式(xml方式)

public class HelloServlet implements Servlet {

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("service  方法");
    }

    @Override
    public String getServletInfo() {
        System.out.println("getServletInfo 方法");
        return null;
    }

    @Override
    public void destroy() {
        System.out.println("destroy 方法");
    }

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("init 初始化方法");
    }

    @Override
    public ServletConfig getServletConfig() {
        System.out.println("getServletConfig 方法");
        return null;
    }
}

web.xml 中的配置:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
<!--給tomcat配置servlet程式-->
<servlet>
<!--servlet程式的別名,一般取類名即可-->
<servlet-name>HelloServlet02</servlet-name>
<!--servlet程式的全限定類名,指向一個繼承了HttpServlet的類-->
<servlet-class>com.lusai.HelloServlet</servlet-class>
</servlet>
<!--給tomcat配置servlet程式的訪問地址-->
<servlet-mapping>
<!--告訴伺服器當前配置的地址給哪個servlet使用,此處和要執行的servlet程式的servlet標籤內servlet-name保持一致-->
<servlet-name>HelloServlet02</servlet-name>
<!--配置訪問地址,如果專案路徑為http://localhost:8080/servlet,則訪問該Demo01程式的路徑為http://localhost:8080/servlet/test-->
<url-pattern>/test01</url-pattern>
</servlet-mapping>
</web-app>

配置Tomcat專案路徑

 

埠號

 

 

 訪問url測試

http://localhost:8080/servlet/test01

看結果

 

 

 Servlet 的生命週期

1、執行 Servlet 構造器方法

2、執行 init 初始化方法 第一、二步,是在第一次訪問的時候建立 Servlet 程式會呼叫。只調用一次

3、執行 service 方法 第三步,每次訪問都會呼叫。

4、執行 destroy 銷燬方法 第四步,在 web 工程停止的時候呼叫

 

通過繼承 HttpServlet 實現 Servlet 程式

一般在實際專案開發中,都是使用繼承 HttpServlet 類的方式去實現 Servlet 程式。

這裡,我們使用Servlet 3.0 新增的註解@WebServlet以避免配置太多的xml

@WebServlet 用於將一個類宣告為 Servlet,該註解將會在部署時被容器處理,容器將根據具體的屬性配置將相應的類部署為 Servlet。

如此配置之後,就可以不必在 web.xml 中配置相應的 <servlet> 和 <servlet-mapping> 元素了,容器會在部署時根據指定的屬性將該類釋出為 Servlet

@WebServlet("/hello2")
public class HelloServlet2 extends HttpServlet {
    /**
     * doGet()在 get 請求的時候呼叫
     */
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("HelloServlet2 的 doGet 方法");
    }
    /**
     * doPost()在 post 請求的時候呼叫
     */
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("HelloServlet2 的 doPost 方法");
    }
}

訪問http://localhost:8080/servlet/hello2

看結果

 

 

 

ServletConfig 類

ServletConfig 類從類名上來看,就知道是 Servlet 程式的配置資訊類。

Servlet 程式和 ServletConfig 物件都是由 Tomcat 負責建立,我們負責使用。

Servlet 程式預設是第一次訪問的時候建立,ServletConfig 是每個 Servlet 程式建立時,就建立一個對應的 ServletConfig 物件。

ServletConfig 類的三大作用

  • 1、可以獲取 Servlet 程式的別名 servlet-name 的值
  • 2、獲取初始化引數 init-param
  • 3、獲取 ServletContext 物件

web.xml 中的配置:

<servlet>
        <servlet-name>HelloServlet</servlet-name>
        <servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>
        <!--init-param 是初始化引數-->
        <init-param>
            <!--是引數名-->
            <param-name>username</param-name>
            <!--是引數值-->
            <param-value>root</param-value>
        </init-param>
        <init-param>
            <param-name>url</param-name>
            <param-value>jdbc:mysql://localhost:3306/test</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>HelloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

Servlet 中的程式碼:

@Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("init 初始化方法");
        // 1、可以獲取 Servlet 程式的別名 servlet-name 的值
        System.out.println("HelloServlet 程式的別名是:" + servletConfig.getServletName());
        // 2、獲取初始化引數 init-param
        System.out.println("初始化引數 username 的值是;" + servletConfig.getInitParameter("username"));
        System.out.println("初始化引數 url 的值是;" + servletConfig.getInitParameter("url"));
        // 3、獲取 ServletContext 物件
        System.out.println(servletConfig.getServletContext());
    }

訪問http://localhost:8080/servlet/hello

看結果

 

 

 獲取到了引數 init-param標籤內的引數

 

ServletContext 類

什麼是 ServletContext?

  • 1、ServletContext 是一個介面,它表示 Servlet 上下文物件
  • 2、一個 web 工程,只有一個 ServletContext 物件例項。
  • 3、ServletContext 物件是一個域物件。
  • 4、ServletContext 是在 web 工程部署啟動的時候建立。在 web 工程停止的時候銷燬。

什麼是域物件? 域物件,是可以像 Map 一樣存取資料的物件,叫域物件。 這裡的域指的是存取資料的操作範圍,整個 web 工程。

ServletContext 類的四個作用

  • 1、獲取 web.xml 中配置的上下文引數 context-param
  • 2、獲取當前的工程路徑,格式: /工程路徑
  • 3、獲取工程部署後在伺服器硬碟上的絕對路徑
  • 4、像 Map 一樣存取資料

web.xml 中的配置:

<!--context-param 是上下文引數(它屬於整個 web 工程)-->
    <context-param>
        <param-name>username</param-name>
        <param-value>context</param-value>
    </context-param>
    <context-param>
        <param-name>password</param-name>
        <param-value>root</param-value>
    </context-param>

ServletContext 演示程式碼:

@WebServlet("/contextServlet")
public class ContextServlet1 extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 獲取 ServletContext 物件
        ServletContext context = getServletContext();
        System.out.println(context);
        System.out.println("儲存之前: Context1 獲取 key1 的值是:" + context.getAttribute("key1"));
        context.setAttribute("key1", "value1");
        System.out.println("Context1 中獲取域資料 key1 的值是:" + context.getAttribute("key1"));
        String username = context.getInitParameter("username");
        System.out.println("context-param 引數 username 的值是:" + username);
        System.out.println("context-param 引數 password 的值是:" + context.getInitParameter("password"));
        // 2、獲取當前的工程路徑,格式: /工程路徑
        System.out.println( "當前工程路徑:" + context.getContextPath() );
    }
    
}

看結果

 

 

 獲取到了 web.xml 中配置的上下文引數 context-param和當前的工程路徑

 

HTTP 協議

什麼是 HTTP 協議 

 

所謂 HTTP 協議,就是指,客戶端和伺服器之間通訊時,傳送的資料,需要遵守的規則,叫 HTTP 協議。 HTTP 協議中的資料又叫報文。

請求的 HTTP 協議格式

客戶端給伺服器傳送資料叫請求。 伺服器給客戶端回傳資料叫響應。 請求又分為 GET 請求,和 POST 請求兩種 

GET 請求

1、請求行

  • (1) 請求的方式 GET
  • (2) 請求的資源路徑[+?+請求引數]
  • (3) 請求的協議的版本號 HTTP/1.1

2、請求頭 key : value 組成 不同的鍵值對,表示不同的含義。

 

 

POST 請求

1、請求行

  • (1) 請求的方式 POST
  • (2) 請求的資源路徑[+?+請求引數]
  • (3) 請求的協議的版本號 HTTP/1.1

2、請求頭

1) key : value 不同的請求頭,有不同的含義

空行

3、請求體 ===>>> 就是傳送給伺服器的資料

 

 響應的 HTTP 協議格式

 

 

 谷歌瀏覽器如何檢視 HTTP 協議:

 

 HttpServletRequest 類

HttpServletRequest 類有什麼作用。

每次只要有請求進入 Tomcat 伺服器,Tomcat 伺服器就會把請求過來的 HTTP 協議資訊解析好封裝到 Request 物件中。 然後傳遞到 service 方法(doGet 和 doPost)中給我們使用。

我們可以通過 HttpServletRequest 物件,獲取到所有請求的 資訊。 

HttpServletRequest 類的常用方法

  • i. getRequestURI() 獲取請求的資源路徑
  • ii. getRequestURL() 獲取請求的統一資源定位符(絕對路徑)
  • iii. getRemoteHost() 獲取客戶端的 ip 地址
  • iv. getHeader() 獲取請求頭
  • v. getParameter() 獲取請求的引數
  • vi. getParameterValues() 獲取請求的引數(多個值的時候使用)
  • vii. getMethod() 獲取請求的方式 GET 或 POST
  • viii. setAttribute(key, value); 設定域資料
  • ix. getAttribute(key); 獲取域資料
  • x. getRequestDispatcher() 獲取請求轉發物件

常用 API 示例程式碼:

@WebServlet("/httpServletRequest")
public class RequestAPIServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // i.getRequestURI() 獲取請求的資源路徑
        System.out.println("URI => " + req.getRequestURI());
        // ii.getRequestURL() 獲取請求的統一資源定位符(絕對路徑)
        System.out.println("URL => " + req.getRequestURL());
        // iii.getRemoteHost() 獲取客戶端的 ip 地址
        /**
         * 在 IDEA 中,使用 localhost 訪問時,得到的客戶端 ip 地址是 ===>>> 127.0.0.1<br/>
         * 在 IDEA 中,使用 127.0.0.1 訪問時,得到的客戶端 ip 地址是 ===>>> 127.0.0.1<br/>
         * 在 IDEA 中,使用 真實 ip 訪問時,得到的客戶端 ip 地址是 ===>>> 真實的客戶端 ip 地址<br/>
         */
        System.out.println("客戶端 ip 地址 => " + req.getRemoteHost());
        // iv.getHeader() 獲取請求頭
        System.out.println("請求頭 User-Agent ==>> " + req.getHeader("User-Agent"));
        // vii.getMethod() 獲取請求的方式 GET 或 POST
        System.out.println("請求的方式 ==>> " + req.getMethod());
    }
}

如何獲取請求引數

表單:

<form action="http://localhost:8080/servlet/parameterServlet" method="get">
使用者名稱:<input type="text" name="username"><br/>
密碼:<input type="password" name="password"><br/>
興趣愛好:<input type="checkbox" name="hobby" value="cpp">C++
<input type="checkbox" name="hobby" value="java">Java
<input type="checkbox" name="hobby" value="js">JavaScript<br/>
<input type="submit">
</form>

Java 程式碼:

@WebServlet("/parameterServlet")
public class ParameterServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 獲取請求引數
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String[] hobby = req.getParameterValues("hobby");
        System.out.println("使用者名稱:" + username);
        System.out.println("密碼:" + password);
        System.out.println("興趣愛好:" + Arrays.asList(hobby));
        //doGet 請求的中文亂碼解決:
        //1 先以 iso8859-1 進行編碼
        //2 再以 utf-8 進行解碼
        username = new String(username.getBytes("iso-8859-1"), "UTF-8");
        System.out.println(username);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 設定請求體的字符集為 UTF-8,從而解決 post 請求的中文亂碼問題
        req.setCharacterEncoding("UTF-8");
        System.out.println("-------------doPost------------");
        // 獲取請求引數
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String[] hobby = req.getParameterValues("hobby");
        System.out.println("使用者名稱:" + username);
        System.out.println("密碼:" + password);
        System.out.println("興趣愛好:" + Arrays.asList(hobby));
    }
}

看結果

 

 請求的轉發

Servlet1 程式碼:

@WebServlet("/servlet1")
public class Servlet1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 獲取請求的引數(辦事的材料)檢視
        String username = req.getParameter("username");
        System.out.println("在 Servlet1(櫃檯 1)中檢視引數(材料):" + username);
        // 給材料 蓋一個章,並傳遞到 Servlet2(櫃檯 2)去檢視
        req.setAttribute("key1","櫃檯 1 的章");
        // 問路:Servlet2(櫃檯 2)怎麼走
        /**
         * 請求轉發必須要以斜槓打頭,/ 斜槓表示地址為:http://ip:port/工程名/ , 對映到 IDEA 程式碼的 web 目錄
         */
        RequestDispatcher requestDispatcher = req.getRequestDispatcher("/servlet2");
        // RequestDispatcher requestDispatcher = req.getRequestDispatcher("http://www.baidu.com");
        // 走向 Sevlet2(櫃檯 2)
        requestDispatcher.forward(req,resp);
    }
}

Servlet2 程式碼:

@WebServlet("/servlet2")
public class Servlet2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 獲取請求的引數(辦事的材料)檢視
        String username = req.getParameter("username");
        System.out.println("在 Servlet2(櫃檯 2)中檢視引數(材料):" + username);
        // 檢視 櫃檯 1 是否有蓋章
        Object key1 = req.getAttribute("key1");
        System.out.println("櫃檯 1 是否有章:" + key1);
        // 處理自己的業務
        System.out.println("Servlet2 處理自己的業務 ");
    }
}

請求http://localhost:8080/servlet/servlet1測試

看結果

 

 請求轉發成功

 

HttpServletResponse 類

HttpServletResponse 類的作用

HttpServletResponse 類和 HttpServletRequest 類一樣。每次請求進來,Tomcat 伺服器都會建立一個 Response 物件傳 遞給 Servlet 程式去使用。

HttpServletRequest 表示請求過來的資訊,HttpServletResponse 表示所有響應的資訊, 我們如果需要設定返回給客戶端的資訊,都可以通過 HttpServletResponse 物件來進行設定

兩個輸出流的說明。

  • 位元組流 getOutputStream(); 常用於下載(傳遞二進位制資料)
  • 字元流 getWriter(); 常用於回傳字串(常用) 兩個流同時只能使用一個。

使用了位元組流,就不能再使用字元流,反之亦然,否則就會報錯。

如何往客戶端回傳資料

@WebServlet("/httpServletResponse")
public class ResponseIOServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //響應的亂碼解決
        // 設定伺服器字符集為 UTF-8
        resp.setCharacterEncoding("UTF-8");
        // 通過響應頭,設定瀏覽器也使用 UTF-8 字符集
        resp.setHeader("Content-Type", "text/html; charset=UTF-8");
        // 它會同時設定伺服器和客戶端都使用 UTF-8 字符集,還設定了響應頭
        // 此方法一定要在獲取流物件之前呼叫才有效
        resp.setContentType("text/html; charset=UTF-8");
        // 要求 : 往客戶端回傳 字串 資料。
        PrintWriter writer = resp.getWriter();
        writer.write("你好 !!!");
    }

}

 

看結果

 

請求重定向

請求重定向,是指客戶端給伺服器發請求,然後伺服器告訴客戶端說。我給你一些地址。你去新地址訪問。叫請求重定向(因為之前的地址可能已經被廢棄)。

@Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //請求重定向的第一種方案:
        // 設定響應狀態碼 302 ,表示重定向,(已搬遷)
        //resp.setStatus(302);
        // 設定響應頭,說明 新的地址在哪裡
        //resp.setHeader("Location", "https://www.baidu.com/");
        //請求重定向的第二種方案(推薦使用):
        resp.sendRedirect("https://www.baidu.com/");
    }

 

測試http://localhost:8080/servlet/httpServletResponse

看結果

 

 

重定向