1. Request與Response
1.1. Web應用運行機制
到目前為止,我們已經掌握了Web應用程序的運行機制,現在學習的就是Web應用程序運行機制中很重要的內容 —— Request與Response。
首先,我們先來看一看Request與Response在Web應用程序運行時,是怎么樣的。
通過上圖的Web應用程序運行機制,我們可以知道關於Request與Response的信息:
- Web應用程序接收一次請求,就創建一個Request對象和Response對象。
- 通過Request對象可以在服務器端獲取客戶端發送的請求數據內容。
- 通過Response對象可以生成服務器端向客戶端響應的數據內容。
- Request對象和Response對象並不是Web應用程序所創建的,而是由Tomcat服務器創建(JavaEE並沒有Request與Response的實現類)。
- JavaEE提供了javax.servlet.http包中提供了HttpServletRequest和HttpServletResponse接口,這兩個接口是繼承於javax.servlet包中的ServletRequest和ServletResponse接口。
- javax.servlet包中的ServletRequest和ServletResponse接口是與協議無關的,而javax.servlet.http包中的HttpServletRequest和HttpServletResponse接口是與Http協議有關的。
1.2. 百度百科
- Request
Request這個對象不用事先聲明,就可以在JSP網頁中使用,在編譯為Servlet之后,它會轉換為javax.servlet.http.HttpServletRequest形態的對象,HttpServletRequest對象是有關於客戶端所發出的請求的對象,只要是有關於客戶端請求的信息,都可以藉由它來取得,例如請求標頭、請求方法、請求參數、客戶端IP,客戶端瀏覽器等等信息。
- Response
Response對象用於動態響應客戶端請示,控制發送給用戶的信息,並將動態生成響應。Response對象只提供了一個數據集合cookie,它用於在客戶端寫入cookie值。若指定的cookie不存在,則創建它。若存在,則將自動進行更新。結果返回給客戶端瀏覽器。
2. 掌握Response
2.1. Response概述
Response是Web應用程序用來封裝向客戶端響應信息的,是Servlet接口的service()方法的一個參數,類型為javax.servlet.http.HttpServletResponse。客戶端每次發送請求時,服務器都會創建一個Response對象,並傳遞給Servlet接口的service()方法,來完成向客戶端的響應工作。
下列是javax.servlet.http.HttpServletResponse常用的API列表:
Method Summary |
|
void |
addDateHeader(String name, long date) |
void |
addHeader(String name, String value) |
void |
sendRedirect(String location) |
void |
setDateHeader(String name, long date) |
void |
setHeader(String name, String value) |
void |
setStatus(int sc) |
下列是javax.servlet.ServletResponse常用的API列表:
Method Summary |
|
getOutputStream() |
|
getWriter() |
|
void |
setCharacterEncoding(String charset) |
void |
setContentType(String type) |
針對HttpServletResponse與ServletResponse常用的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概述
Request是Web應用程序用來封裝向客戶端請求信息的,是Servlet接口的service()方法的一個參數,類型為javax.servlet.http.HttpServletRequest。客戶端每次發送請求時,服務器都會創建一個Request對象,並傳遞給Servlet接口的service()方法,來完成向客戶端的請求工作。
下列是javax.servlet.http. HttpServletRequest常用的API列表:
Method Summary |
|
getContextPath() |
|
long |
getDateHeader(String name) |
getHeader(String name) |
|
getHeaderNames() |
|
getHeaders(String name) |
|
int |
getIntHeader(String name) |
getMethod() |
|
getPathInfo() |
|
getQueryString() |
|
getRequestURI() |
|
getRequestURL() |
|
getServletPath() |
下列是javax.servlet.ServletRequest常用的API列表:
Method Summary |
|
getCharacterEncoding() |
|
getContentType() |
|
getParameter(String name) |
|
getProtocol() |
|
getRealPath(String path) |
|
getRemoteAddr() |
|
getRemoteHost() |
|
int |
getRemotePort() |
getRequestDispatcher(String path) |
|
getServerName() |
|
int |
getServerPort() |
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請求協議的請求方式,例如GET或POST等。
- 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請求協議中的請求方式常用的有兩種GET和POST,GET方式的請求參數在請求行中的資源路徑后面,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工具查看請求時,只發生了一次請求。