服務器每次收到請求時,都會為這個請求開闢一個新的線程。 服務器會把客戶端的請求數據封裝到 request 對象中,request 就是請求數據的載體。(EX: 袋子) 服務器還會創建 response 對象,這個對象與客戶端連接在一起,它可以用來向客戶端發送響應。(EX: 手機)
- ServletResponse -> 與協議無關的類型。 HttpServletResponse -> 與 http 協議相關的類型。
- 狀態碼: 200 表示成功,302 表示重定向,404 表示客戶端錯誤(訪問資源不存在),500 表示服務端錯誤。
- sendError(int sc): 發送錯誤狀態碼(EX: 404, 500)
- sendError(int sc, String msg): 發送錯誤狀態碼,可以帶錯誤信息。
- setStatus(int sc): 發送成功狀態碼(EX: 302)。
- 案例: 發送 404
- 響應頭: Content-Type, Refresh, Location 等。 頭就是一個鍵值對,可能會存在一個頭(一個名稱,一個值),也可能會存在一個頭(一個名稱,多個值)
- setHeader(String name, String value): 適用於單值的響應頭,例如: response.setHeader("aaa",AAA");
- addHeader(String name, String value): 適用於多值的響應頭。
- setIntHeader(String name, int value): 適用於單值的 int 類型的響應頭。
- addIntHeader(String name, int value): 適用於多值的 int 類型的響應頭。
- setDateHeader(String name, long value): 適用於單值得毫秒類型的響應頭。
- addDateHeader(String name, long value): 適用於多值的毫秒類型的響應頭。
- 案例:
- 發送 302, 設置 Location 頭,完成重定向。
- 定時刷新,設置 Refresh 頭。
- 禁用瀏覽器緩存: Cache-Control, pragma, expires
- meta 標籤可以代替響應頭。
- 響應體: 通常是 html, 也可以是圖片。
- response 的兩個流: - ServletOutputStream: 用來向客戶端發送字節數據。 - PrintWriter: 用來向客戶端發送字符數據,需要設置編碼。 - 兩個流不能同時使用。
- 案例: - 使用 PrintWriter 發送字符數據。 - 使用 ServletOutputStream 發送字節數據(圖片)。
- 重定向 -> 設置 302,設置 Location。 其中變化的只有 Location,所以 Java 提供了一個快捷方法完成重定向。
- sendRedirect(String location) 方法
/** * 演示重定向 * @author asus * * 1. 用戶請求 ServletDemo2,然後 ServletDemo2 響應 302, 給出 Location 頭 */ public class ServletDemo2 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("ServletDemo2"); /* * 重定向: * 1. 設置 Location * 2. 發送 302 狀態碼 */ // response.setHeader("Location", "/Java_Web/servlet_2/ServletDemo3"); // response.setStatus(302); //快捷的重定向方法 response.sendRedirect("Java_Web/servlet_2/ServletDemo3/"); } } /** * 瀏覽器會重定向到這 * @author asus */ public class ServletDemo3 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("ServletDemo3"); } }
請求行 請求頭 空行 請求體 回憶 http 協議,請求協議中的數據都可以通過 request 對象來獲取。
- 獲取常用信息
- 獲取客戶端 IP,案例: 封鎖 IP -> request.getRemoteAddr()
- 請求方式
- 獲取http請求頭
- String getHeader(String name),適用於單值頭
- int getIntHeader(String name),適用於單值 int 類型的請求頭
- long getDateHeader(String name),適用於單值毫秒類型的請求頭
- Enumeration getHeaders(String name),適用於多值請求頭
- 案例:
- 通過 User-Agent 識別用戶瀏覽器類型
- 防盜鏈: 如果請求不是通過本站的超鏈接發出的,發送錯誤狀態碼 404。-> Referer 這個請求頭,表示請求的來源。
public class ServletDemo3 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
* 使用 Referer 請求頭,來防盜鏈
*/
String referer = request.getHeader("Referer");
System.out.println(referer);
if(referer == null || !referer.contains("localhost")) {
System.out.println("Hello");
} else {
response.sendRedirect("http://www.baidu.com");
}
}
}
- Example
/** * 演示: 獲取客戶端的 ip 地址、獲取請求方式、獲取 User-Agent,得到客戶端信息(操作系統瀏覽器) * @author asus */ public class ServletDemo extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String ip = request.getRemoteAddr();//獲取客戶端的 ip 地址 System.out.println("IP: " + ip); System.out.println("請求方式: " + request.getMethod());//獲取請求方式 String userAgent = request.getHeader("User-Agent");//獲取名為 User-Agent 的請求頭 /* * Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36 */ //是否包含 Chrome,如果包含,說明用戶使用的是 google 瀏覽器 if(userAgent.toLowerCase().contains("chrome")) { System.out.println("Hello " + ip + ", you are using google"); } else if(userAgent.toLowerCase().contains("firefox")) { System.out.println("Hello " + ip + ", you are using firefox"); } else if(userAgent.toLowerCase().contains("msie")) { System.out.println("Hello " + ip + ", you are using msie"); } System.out.println(userAgent); } }
- 獲取請求 URL
http://localhost:8080/Java_Web/servlet_3/ServletDemo?username=xxx&password=yyy
- String getScheme(): 獲取協議 -> http
- String getServerName(): 獲取服務器名 -> localhost
- String getServerPort(): 獲取服務器端口 -> 8080
- String getContextPath(): 獲取項目名 -> /Java_Web
- String getServletPath(): 獲取 Servlet 路徑 -> /servlet_3/ServletDemo
- String getQueryString(): 獲取參數部分,即問號後面的部分。-> username=xxx&password=yyy
- String getRequestURI(): 獲取請求 URI,等於項目名 + Servlet 路徑 -> /servlet_3/ServletDemo
- String getRequestURL(); 獲取請求 URL,等於不包含參數的整個請求路徑。-> http://localhost:8080/Java_Web/servlet_3/ServletDemo
public class ServletDemo2 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /* * 通過 request 來獲取 url 的相關方法 */ response.getWriter().println(request.getScheme());//獲取請求協議 response.getWriter().println(request.getServerName());//獲取服務器名稱 response.getWriter().println(request.getServerPort());//獲取服務器端口號 response.getWriter().println(request.getContextPath());//獲取項目名稱 response.getWriter().println(request.getServletPath());//獲取 Servlet 路徑 response.getWriter().println(request.getQueryString());//獲取參數部分 response.getWriter().println(request.getRequestURI());//獲取請求 URI response.getWriter().println(request.getRequestURL());//獲取請求 URL } }
http
localhost
8080
/Java_Web
/servlet_3/ServletDemo2
username=jack&password=123
/Java_Web/servlet_3/ServletDemo2
http://localhost:8080/Java_Web/servlet_3/ServletDemo2
-
獲取請求參數: 請由參數是由客戶端發送給服務器的,有可能是在請求體中 (POST),也可能是在 URL 之後 (GET)
/** * 演示 request 請求參數 * @author asus */ public class ServletDemo4 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("GET: " + request.getParameter("xxx")); System.out.println("GET: " + request.getParameter("yyy")); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username"); String password = request.getParameter("password"); String[] hobby = request.getParameterValues("hobby"); System.out.println(username + ", " + password + ", " + Arrays.toString(hobby)); /* * 測試獲取所有請求參數的名稱 */ Enumeration names = request.getParameterNames(); while(names.hasMoreElements()) { System.out.println(names.nextElement()); } /* * 獲取所有請求參數 */ Map<String, String[]> map = request.getParameterMap(); for(String name : map.keySet()) { String[] values = map.get(name); System.out.println(name + " @ " + Arrays.toString(values)); } } }
- String getParameter(String name): 獲取指定名稱的請求參數值,適用於單值請求參數。
-
String[] getParameterValues(String name): 獲取指定名稱的請求參數值,適用於多值請求參數。
-
Enumeration getParameterNames(): 獲取所有請求參數名稱。
-
Map getParameterMap(): 獲取所有請求參數,其中 key 為參數名,value 為參數值。
-
案例: 超鏈接參數
-
案例: 表單數據
-
請求轉發和請求包含 RequestDispatcher rd = request.getRequestDispatcher("/MyServlet"); 使用 request 獲取 RequestDispatcher 對象,方法的參數是被轉發或包含的 Servlet 的 Servlet 路徑。 請求轉發: rd.forward(request, response); -> 常用
/** * 演示請求轉發 * @author asus */ public class ServletDemo extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("SerlvetDemo..."); response.setHeader("aaa", "AAA");//設置響應頭 // for(int i = 0; i < 1024 * 24 + 1; i++) { // response.getWriter().print("a");//設置響應體 // } //等同於調用 ServletDemo2 的 service() 方法 request.getRequestDispatcher("/ServletDemo2").forward(request, response);//請求轉發 } } public class ServletDemo2 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("ServletDemo2..."); response.getWriter().print("Hello ServletDemo2!");//設置響應體 } }
請求包含: rd.include(request, response);
/** * 演示請求包含 * @author asus */ public class ServletDemo extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("ServletDemo..."); response.setHeader("aaa", "AAA");//設置響應頭 response.getWriter().print("aaaa");//設置響應體 request.getRequestDispatcher("/ServletDemo2").include(request, response);//請求轉發 } } public class ServletDemo2 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("ServletDemo2"); response.getWriter().print("Hello ServletDemo2!");//設置響應體 } }
有時一個請求需要多個 Serlvet 協作才能完成,所以需要再一個 Servlet 跳到另一個 Servlet
- 一個請求跨多個 Serlvet,需要使用轉法和包含。
- 請求轉發,由下一個 Servlet 完成響應體,當前 Servlet 可以設置響應頭。(留頭不留體)
- 請求包含,由兩個 Servlet 共同完成響應體。(都留)
- 無論是請求轉發還是請求包含,都在一個請求範圍內,使用同個 request & response
-
request 域 Servlet 中三大域對象: request, session, application,都有如下三個方法:
-
void setAttribute(String name, Object value)
-
Object getAttribute(String name)
-
void removeAttribute(String name)
-
同一請求範圍內使用 request.setAttribute()、request.getAttribute() 來傳值,前一個 Servlet 調用 setAttribute() 保存值,後一個 Servlet 調用 getAttribute()
-
請求轉發和重定向的區別
-
請求轉發是一個請求一次響應,而重定向是兩次請求兩次響應。
-
請求轉發地址欄不變化,而重定向會顯示後一個請求的地址。
-
請求轉發只能轉發到本項目其他 Servlet,而重定向不只能重定向到本項目的其他 Servlet,還能定向到其他項目。
-
請求轉發是服務器端行為,只須出轉發的 Servlet 路徑,而重定向需要給出 requestURI,即包含項目名。
-
請求轉發效率較高,因為是一個請求。
-
需要地址欄發生變化,必須使用重定向。
-
需要在下一個 Servlet 中獲取 request 域中的數據,必須使用轉發。