文章目錄
- HttpServletRequest請求域接口
- HttpServletRequest請求域接口簡介
- 關于請求域和應用域的區別
- 請求域接口中的相關方法
- 獲取前端請求參數(getParameter系列方法)
- 存儲請求域名參數(Attribute系列方法)
- 獲取客戶端的相關地址信息
- 獲取項目的根路徑
- 關于轉發和重定向的細致剖析
- 轉發代碼實現及相關問題
- 重定向代碼實現及相關問題
HttpServletRequest請求域接口
HttpServletRequest請求域接口簡介
其實關于請求域這個詞也蠻熟悉的, 因為我們之前學習過 應用域
這一概念, 應用域的生命周期很長, 伴隨這服務器的啟動和終止, 作用范圍也很廣, 對所有的處于當前 webapp
也就是 web 應用的所有Servlet對象都生效
-
HttpServletRequest
是位于jakarta.servlet.http.*
包下面的一個接口 -
繼承了
ServletRequest接口
public interface HttpServletRequest extends ServletRequest
-
之前我們學習過HTTP協議的相關內容, 這個對象中封裝的其實就是網絡傳輸的時候, 發送的HTTP請求(Request)中封裝的相關參數內容信息
-
實現這個接口是Tomcat服務器實現的, 傳遞對象封裝參數也是Tomcat服務器完成好的內容, 我們作為Java程序員, 只需要學習獲取其中封裝的相關參數即可
關于請求域和應用域的區別
- 生命周期不同, 應用域伴隨著Tomcat的生命周期 而 請求域 只作用域這一次請求之內, 而且http協議的特點就是, 一次請求一次創建一次請求域對象
- 而且在進行參數設定的時候, 盡量的去選擇請求域的參數而不是應用域的參數, 因為小的域的對象占用的資源比較小
請求域接口中的相關方法
上面都說了, 請求域是封裝了相關的http協議的參數信息, 所以必定提供了一些方法來讓我們程序員獲取到這些參數的信息…
獲取前端請求參數(getParameter系列方法)
首先我們思考, 前端傳遞過來的參數應該采用什么數據結構來組織比較好
我們從下面的前端的頁面中獲取信息
<!DOCTYPE html>
<html lang='en'>
<head><meta charset='UTF-8'><meta name='viewport' content='width=device-width, initial-scale=1.0'><title>個人信息</title>
</head>
<body><h2>個人信息</h2><form action="" method="get">姓名:<input type="text" name="name" value=""><br>年齡:<input type="text" name="age" value=""><br>性別:<input type="radio" name="sex" value="男"><input type="radio" name="sex" value="女"><br>愛好:<input type="checkbox" name="hobby" value="吃飯"><input type="checkbox" name="hobby" value="睡覺"><input type="checkbox" name="hobby" value="打游戲"><br><input type="submit" value="提交"></form>
</body>
</html>
我們執行 http://127.0.0.1:8080/servlet08/test.html
我們對URL拆解如下(涉及URLEncoding)
我們可以發現, 前端向后端提交數據的格式其實并不是單純的鍵值對的結構存儲的, 因為如果是鍵值對結構存儲的話, 一個key只能對應一個value, 但是復選框這種提交信息的結構, 一個key可以對應多個value信息
所以實際上, 我們的前端發來的數據存儲格式是一個特殊的map集合
Map<String, String[]> map = new HashMap<>();
一個String類型的key可以對應一個String[] 數組, 也就是多個value(前端傳遞參數都是String類型)
上面是一個html頁面, 其中包含test類型文本, 單選框, 復選框
上面的方法的解釋:
- String getParameter(String name): 根據key返回String數組中的第一個參數
- String[] getParameterValues(String name): 根據key返回完整的String[]數組
- Enumeration< String > getParameterNames(): 返回一個由所有key組成的集合
- Map<String, String[]> getParameterMap(): 返回一個key和value組成的完整的集合
還拿我們上面寫的那個html頁面進行測試(設置一下傳遞的Servlet路徑地址)
(注意, 我們下面的method其實寫錯了, 實際上是post請求)
Servlet對象的源碼如下
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;import java.io.*;
import java.util.Enumeration;
import java.util.Map;@WebServlet(urlPatterns = "/getparameter")
public class GetParameterServlet extends HttpServlet {// 由于是form表單提交的數據, 我們盡量采用重寫doPost的方式進行測試@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 最好還是設置一下字符集, 防止出現亂碼response.setContentType("text/html;charset=UTF-8");PrintWriter out = response.getWriter();// 1. 使用getParameterMap獲取整個的map形式參數集合out.print("<h3>使用getParameterMap獲取整個集合</h3>");Map<String, String[]> parameterMap = request.getParameterMap();for(Map.Entry<String, String[]> entry : parameterMap.entrySet()){out.print(entry.getKey() + "=");for(String value : entry.getValue()){out.print(value + " ");}}out.print("<br>");out.print("====================================<br>");// 2. 使用getParameterNames獲取整個參數集合的keyout.print("<h3>使用getParameterNames獲取整個集合中的key</h3>");Enumeration<String> parameterNames = request.getParameterNames();while(parameterNames.hasMoreElements()){String name = parameterNames.nextElement();out.print(name + " ");}out.print("<br>");out.print("====================================<br>");// 3. 使用getParameterValues, 根據key獲取參數集合的value數組String[] hobbys = request.getParameterValues("hobby");out.print("hobby=");for(String hobby : hobbys){out.print(hobby + " ");}out.print("<br>");out.print("====================================<br>");// 4. 使用getParameter, 根據key獲取到value數組中的第一個值String name = request.getParameter("name");out.print("name=" + name);out.print("<br>");out.print("====================================<br>");}
}
測試:
下面是form表單中提交的數據信息
下面是在瀏覽器中輸出的內容
存儲請求域名參數(Attribute系列方法)
Attribute
這個詞其實我們很熟悉了, 因為之前學習ServletContext
就出現過這個詞, 也出現了和下面一模一樣的一系列方法, 當時是設置應用域對象, 但是現在是設置請求域對象
- void setAttribute(String name, Object o): 設置請求域參數
- Object getAttribute(String name): 獲取請求域參數
- Enumeration< String > getAttributeNames(): 獲取所有請求域的key組成的集合
- void removeAttribute(String name): 移除 key 為參數的請求域信息
沒啥可說的, 直接上測試代碼
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;import java.io.*;
import java.util.Enumeration;// 使用注解來配置Servlet
@WebServlet("/attribute")
public class AttributeInfoServlet extends HttpServlet {// 重寫doGet方法@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {response.setContentType("text/html");PrintWriter out = response.getWriter();// 1. 使用 void setAttribute(String name, Object o) 設置請求域參數request.setAttribute("name", "Jack");request.setAttribute("age", 18);// 2. 使用 Object getAttribute(String name) 獲取請求域參數Object name = request.getAttribute("name");Object age = request.getAttribute("age");out.print("<h3>" + name + " " + age + "</h3>");out.print("<br>========================================<br>");// 3. 使用 Enumeration< String > getAttributeNames() 獲取所有的請求域參數key集合Enumeration<String> attributeNames = request.getAttributeNames();while (attributeNames.hasMoreElements()) {String attributeName = attributeNames.nextElement();out.print("<h3>" + attributeName + "</h3>");}out.print("<br>========================================<br>");// 4. 使用 void removeAttribute(String name) 移除參數request.removeAttribute("name");Object name1 = request.getAttribute("name");out.print("<h3>" + name1 + "</h3>");}
}
測試結果如下
獲取客戶端的相關地址信息
我們需要掌握下面的三個獲取地址相關信息的方法
- getRemoteAddr(): 獲取客戶端主機IP
- getRemotePort(): 獲取客戶端的應用端port(端口號)
- getRemoteHost(): 獲取客戶端主機名稱
下面是關于上面的三個方法的測試代碼(我們直接給出)
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;import java.io.*;// 使用注解代替web.xml進行Servlet的配置
@WebServlet(urlPatterns = "/addr")
public class GetAddr extends HttpServlet {// 重寫doGet方法@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 設置返回的類型以及獲取輸出流信息response.setContentType("text/html");PrintWriter out = response.getWriter();// 1. 使用getRemoteAddr獲取客戶端的IP信息String remoteAddr = request.getRemoteAddr();out.print("<h3>客戶端的IP地址為</h3><br>");out.print(remoteAddr);out.print("<br>=====================================");// 2. 使用getRemotePort獲取客戶端的端口號的信息int remotePort = request.getRemotePort();out.print("<h3>客戶端的端口號為</h3><br>");out.print(remotePort);out.print("<br>======================================");// 3. 使用getRemoteHost獲取客戶端的主機名稱String remoteHost = request.getRemoteHost();out.print("<h3>客戶端的主機名號為</h3><br>");out.print(remoteHost);}
}
在瀏覽器上面訪問這個資源, 可以得到下面的內容
注意我們的端口號其實不是固定的, 每一次請求的端口號都是在一個范圍之內進行隨機的, 因為我們的規范建議客戶端的端口號設置為變化的, 服務器端的端口號設置為不變的…
獲取項目的根路徑
這個方法其實用的還是很多的, 因為我們在大量的場景中都需要動態獲取根路徑, 也就是項目路徑, 我們在先前的內容中其實也提到過這個方法…
- getContextPath(): 獲取項目部署的路徑…
測試就省略了, 主要是想說這個方法的作用非常的重要, 我們好多地方獲取項目的路徑都需要這個方法…
關于轉發和重定向的細致剖析
首先要了解, 不管是轉發和重定向, 其目的都是為了實現資源的跳轉
也就是Java中有兩種方式實現資源的跳轉
- 轉發
- 重定向
轉發代碼實現及相關問題
getRequestDispatch(String servletName)
: 通過給定的轉發的ServletName地址獲取一個分發器對象forward(request, response)
: 把當前Servlet對象的請求響應對象作為參數傳遞到轉發當中去, 從而實現位于同一個請求域的作用…
轉發的代碼實現
首先創建一個AServlet對象(相關注釋都在代碼中)
import bean.User;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;import java.io.*;// 使用注解信息簡化Servlet配置
// 我們把這個 AServlet 作為資源訪問的入口, 然后對 BServlet進行資源的轉發(所以二者本質上還是一次請求, 共享同一個請求域)
@WebServlet(urlPatterns = "/a")
public class AServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 因為要測試轉發是不是在一次請求之內轉發(也就是多個Servlet共享同一個request和response對象)// 我們設置相關的請求域參數(我們把用戶定義在了另一個包當中, 等會我們復制代碼就不展示User類了, 應該可以看懂)request.setAttribute("user", new User("huahua", "19", "zz"));// 獲取分發器對象, 調用分發器對象的forward方法對這次請求進行轉發RequestDispatcher requestDispatcher = request.getRequestDispatcher("/b");requestDispatcher.forward(request, response);}
}
創建一個BServlet對象作為AServlet的轉發請求的地址
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import bean.User;import java.io.*;// 使用注解簡化Servlet配置
// 這個BServlet作為轉發的接收方, 接收AServlet的轉發
@WebServlet(urlPatterns = "/b")
public class BServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {response.setContentType("text/html");PrintWriter out = response.getWriter();// 獲取AServlet中的應用域的參數Object user = request.getAttribute("user");// 強制類型轉化User us = (User) user;// 輸出其中的信息內容out.print(us);}
}
我們現在在瀏覽器中訪問AServlet的資源, 顯然, 跳轉到了BServlet中
- 可以發現, 在A中設置的請求域參數, B中同樣可以獲取, 所以可以判定二者位于同一個請求域
- 通過URL可以發現, 雖然資源跳轉到了BServlet, 但是URL中的地址還是顯示的AServlet的地址, 所以我們可以了解到, 其實轉發是一種Tomcat服務器內部進行的資源跳轉, 和瀏覽器無關(
和重定向區分的重要依據
) - 根據上面的提示, 我們可以了解到, 轉發是同一次請求的轉發, 也就是只能在一種方法當中之間進行轉發, 全部都在doGet內部轉發, 或者全部都在doPost請求中進行轉發…
轉發不可以在不同的方法之間完成跳轉, 測試如下
假設我們把 BServlet中的 doGet
方法轉換為 doPost
方法
其他代碼完全不變, 此時再次向AServlet發送請求
會發現直接報錯, 報錯信息是 405 method not allowed
其實針對上述問題, 我們還是有解決方案的, 只需要在doGet方法內部調用doPost就可以避免這種問題
繼續訪問AServlet, 會發現程序還是可以正常執行
重定向代碼實現及相關問題
和轉發不同, 重定向調用的API位于response對象中
通過 response
對象調用 sendReDirect(/項目路徑/Servlet路徑)
方法進行重定向
代碼測試
CServlet如下
import bean.User;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;import java.io.*;// 使用注解簡化開發
@WebServlet(urlPatterns = "/c")
public class CServlet extends HttpServlet {// 重寫doGet方法@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 在CServlet類中設置請求域參數, 然后在DServlet中獲取這個請求域參數, 查看是否可以獲取得到...request.setAttribute("user", new User("huahua", "19", "zz"));// 調用 sendRedirt 進行重定向操作(重定向要加上項目的地址), 重定向至DServletresponse.sendRedirect(request.getContextPath() + "/d");}
}
DServlet
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;import java.io.*;// 使用注解簡化開發
@WebServlet(urlPatterns = "/d")
public class DServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {response.setContentType("text/html");PrintWriter out = response.getWriter();// 嘗試接收CServlet中設置的請求域參數, 查看是不是可以獲取到(其實本質是查看是不是一次請求)Object user = request.getAttribute("user");out.println(user == null ? "不是一個請求" : "是一個請求");}
}
向CServlet發送請求
獲取響應結果如下
- 很明顯的看到URL中的資源明顯的發生了改變
我們不妨抓個包看一看剛才發生了什么
會發現出現了兩次請求…
第一次向CServlet發送了請求, 這是第一次的請求響應信息
可以發現, 響應時的狀態碼是 302 Found
也就是發生了重定向的操作
第二次請求是直接通過瀏覽器向DServlet發送了一個請求而不是Tomcat資源內部的跳轉, 具體不再演示了