request是Servlet.service()方法的一個參數,在客戶端發出每一個請求時,服務器都會建立一個request對象,並把請求數據封裝到request中,而後在調用Servlet.service()方法時傳遞給service()方法html
HttpServletRequest對象表明客戶端的請求,當客戶端經過HTTP協議訪問服務器時,HTTP請求頭中的全部信息都封裝在這個對象中,開發人員經過這個對象的方法,能夠得到客戶這些信息java
存儲web
//用來存儲一個對象,也能夠稱之爲存儲一個域屬性 void setAttribute(String name, Object value) Eg:servletContext.setAttribute(「xxx」, 「XXX」) //在ServletContext中保存了一個域屬性,域屬性名稱爲xxx,域屬性的值爲XXX
獲取數組
//用來獲取ServletContext中的數據 Object getAttribute(String name) //獲取名爲xx的域屬性 Eg:String value = (String)servletContext.getAttribute(「xxx」); //獲取全部域屬性的名稱; Enumeration getAttributeNames()
移除瀏覽器
//用來移除ServletContext中的域屬性 void removeAttribute(String name)
//獲取指定名稱的請求頭 String getHeader(String name) //獲取全部請求頭名稱 Enumeration getHeaderNames() //獲取值爲int類型的請求頭 int getIntHeader(String name)
//獲取請求體的字節數,GET請求沒有請求體,沒有請求體返回-1; int getContentLength() /* 獲取請求類型,若是請求是GET,那麼這個方法返回null;若是是POST請求,那麼默認 爲application/x-www-form-urlencoded,表示請求體內容使用了URL編碼; */ String getContentType() //返回請求方法,例如:GET/POST String getMethod() //返回當前客戶端瀏覽器的Locale。java.util.Locale表示國家和言語,這個東西在國際化中頗有用; Locale getLocale() /* 獲取請求編碼,若是沒有setCharacterEncoding(),那麼返回null,表示使用 ISO-8859-1編碼; */ String getCharacterEncoding() /* 設置請求編碼,只對請求體有效!注意,對於GET而言,沒有請求體!!!因此此方法 只能對POST請求中的參數有效! */ void setCharacterEncoding(String code) //返回上下文路徑,例如:/Dmoe1 String getContextPath() //返回請求URL中的參數,例如:username=zhangSan String getQueryString() //返回請求URI路徑,例如:/Demo1/ServletDemo1 String getRequestURI() /* 返回請求URL路徑,例如:http://localhost/Demo1/ServletDemo1即返回除了參數 之外的路徑信息; */ StringBuffer getRequestURL() //返回Servlet路徑,例如:/ServletDemo1 String getServletPath() //返回當前客戶端的IP地址 String getRemoteAddr() //返回當前客戶端的主機名,但這個方法的實現仍是獲取IP地址 String getRemoteHost() //返回請求協議,例如:http String getScheme() //返回主機名,例如:localhost String getServerName() //返回服務器端口號,例如:8080 int getServerPort()
爲了方便記憶,咱們畫一張圖輔助記憶安全
案例一:防盜鏈服務器
顧名思義,就是說讓用戶只能在咱們站內訪問對應網頁,而經過複製連接到地址欄以及貼連接到別人的網站進行盜鏈,則所有跳轉回本身的連接頁面app
注意:有一部分響應代碼未接觸,可先照着敲,大體體會,後期回來看webapp
先看一下效果:jsp
這是咱們所制定的網站,簡單理解爲官網
在官網中,正常點擊連接訪問,頁面跳轉正常
若是咱們本地寫一個頁面,直接繞過 a.html 去訪問 http://localhost:8080/web-001/ServletDemo3"
此時頁面就會跳轉回a.html中去,也就會回到了咱們的官網,而且控制檯輸出:非法盜鏈,已經跳回原頁面訪問!
下面是具體的代碼實現
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <a href="/web-001/ServletDemo3">葫蘆娃最新資源!!!</a> </body> </html>
package cn.ideal.web.request; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/ServletDemo3") public class RequestDemo1 extends HttpServlet { public RequestDemo1() { super(); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //獲取網頁來源 String referer = req.getHeader("referer"); //非法盜鏈 if (referer == null || !referer.contains("localhost:8080/web-001/a.html")) { System.out.println("非法盜鏈,已經跳回原頁面訪問!"); resp.sendRedirect("a.html"); return; } //正常訪問 resp.setContentType("text/html;charset=UTF-8"); resp.getWriter().write("是他就是他,是他就是他,咱們的英雄葫蘆娃!!!"); } }
//根據參數名稱獲取參數值 String getParameter(String name) //根據參數名稱獲取參數值的數組 String[] getParameterValues(String name) //獲取全部請求的參數名稱 Enumeration<String> getParameterNames() //獲取全部參數的map集合 Map<String,String[]> getParameterMap()
(一)表單提交數據【經過post方式提交數據】
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <form action="/web-001/RequestDemo3" method="post"> <table> <tr> <td>用戶名</td> <td><input type="text" name="username"></td> </tr> <tr> <td>密碼</td> <td><input type="password" name="password"></td> </tr> <tr> <td>性別</td> <td><input type="radio" name="gender" value="男">男 <td><input type="radio" name="gender" value="女">女 </td> </tr> <tr> <td>愛好</td> <td> <input type="checkbox" name="hobbies" value="游泳">游泳 <input type="checkbox" name="hobbies" value="跑步">跑步 <input type="checkbox" name="hobbies" value="網球">網球 </tr> <input type="hidden" name="aaa" value="this is hidden text!"> <tr> <td>從哪來的?</td> <td> <select name="address"> <option value="廣州">廣州</option> <option value="北京">北京</option> <option value="深圳">深圳</option> </select> </td> </tr> <tr> <td>補充說明</td> <td> <textarea rows="2" cols="30" name="textarea"></textarea> </td> </tr> <tr> <td><input type="submit" value="提交"></td> <td><input type="reset" value="重置"></td> </tr> </table> </form> </body> </html>
package cn.ideal.web.request; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Arrays; @WebServlet("/RequestDemo3") public class RequestDemo3 extends HttpServlet { public RequestDemo3() { super(); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //設置request字符編碼的格式 req.setCharacterEncoding("UTF-8"); //經過html的name屬性,獲取到值 String username = req.getParameter("username"); String password = req.getParameter("password"); String gender = req.getParameter("gender"); //複選框和下拉框有多個值,獲取到多個值 String[] hobbies = req.getParameterValues("hobbies"); String[] address = req.getParameterValues("address"); //獲取到文本域的值 String description = req.getParameter("textarea"); //獲得隱藏域的值 String hiddenValue = req.getParameter("aaa"); System.out.println("username: " + username); System.out.println("password: " + password); System.out.println("gender: " + gender); System.out.println("hobbies: " + Arrays.toString(hobbies)); System.out.println("address: " + Arrays.toString(address)); System.out.println("description: " + description); System.out.println("hiddenValue: " + hiddenValue); } }
(二)超連接方式提交數據【經過get方式提交數據】
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <hr/> <form action="/web-001/RequestDemo4" method="get"> 參數1:<input type="text" name="p1"/><br/> 參數2:<input type="text" name="p2"/><br/> <input type="submit" value="提交"/> </form> </body> </html>
//省略包 @WebServlet("/RequestDemo4") public class RequestDemo4 extends HttpServlet { public RequestDemo4() { } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String v1 = req.getParameter("p1"); String v2 = req.getParameter("p2"); System.out.println("p1=" + v1); System.out.println("p2=" + v2); } }
亂碼問題主要針對Tomcat8之前的版本,Tomcat8以上版本默認編碼格式是UTF-8,而不是ISO 8859-1了
//設置request字符編碼的格式 request.setCharacterEncoding("UTF-8");
Tomcat服務器的默認編碼是ISO 8859-1,而瀏覽器使用的是UTF-8編碼。瀏覽器的中文數據提交給服務器,Tomacat以ISO 8859-1編碼對中文編碼,當我在Servlet讀取數據的時候天然拿到亂碼。因此設置request的編碼爲UTF-8,亂碼就解決了
注意:按照上述例子中(使用post方式)亂碼問題已經解決了,可是在get方式中嘗試仍然是亂碼。在此咱們須要瞭解post方法是怎麼進行參數傳遞的。
當咱們點擊提交按鈕的時候,數據封裝進了Form Data中,http請求中把實體主體帶過去了【傳輸的數據稱之爲主體】,既然request對象封裝了http請求,因此request對象能夠解析到發送過來的數據,因而只要把編碼設置成UTF-8就能夠解決亂碼問題
(對上例中post請求方式進行抓包)
而get方式不一樣,它的數據是從消息行帶過去的,沒有封裝到request中,因此使用request設置編碼是無效的。
解決方法: 咱們既然知道Tomcat默認的編碼是ISO 8859-1,那麼get方式由消息體帶過去給瀏覽器的時候確定是用ISO 8859-1編碼了。
(還能夠經過修改Tomcat服務器的配置來解決,可是不推薦,由於會太依賴服務器了)
//此時獲得的數據已是被ISO 8859-1編碼後的字符串了,這個是亂碼 String name = request.getParameter("username); //亂碼經過反向查ISO 8859-1獲得原始的數據 byte[] bytes = name.getBytes("ISO 8859-1); //經過原始的數據,設置正確的碼錶,構建字符串 String value = new String(bytes,"UTF-8");
服務器內部的資源跳轉方式
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <form action="/web-001/RequestDemo" method="get"> <h1>這是轉發後的首頁,地址欄地址也沒有發生變化</h1> </form> </body> </html>
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //獲取到requestDispatcher對象,跳轉到c.html RequestDispatcher requestDispatcher = request.getRequestDispatcher("/c.html"); //調用requestDispatcher對象的forward()實現轉發,傳入req和resp方法 requestDispatcher.forward(reqt, resp);
轉發的結果就是地址欄沒有發生變化,可是頁面已經跳轉到c.html頁面
學習Response後咱們會學習重定向問題,到時候與轉發作區分對別,請留意這一部分
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { request.setAttribute("username", "admin"); //獲取到requesetDispatcher對象 RequestDispatcher requestDispatcher = req.getRequestDispatcher("/servletB"); //調用requestDispatcher對象的forward()實現轉發,傳入req和resp方法 requestDispatcher.forward(req, resp); }
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //獲取到存進requeset對象的值 String username = (String)req.getAttribute("username"); //在瀏覽器輸出該值 respe.getWriter().write("i am: " + username); }
咱們能夠同時使用ServletContext和request實現Servlet之間的通信
通常來講咱們儘可能使用request,由於ServletContext表明着整個web應用,使用ServetContext會消耗大量的資源,而request對象會隨着請求的結束而技術,資源會被回收,使用request域進行Servlet進行Servlet之間的通信在開發中是很是頻繁的
細節:
若是在調用foreard方法以前,在Servlet程序中寫入的部分已經被真正地傳到了客戶端,forward方法將拋出IllegalStateException異常,也就是說,不要在在轉發以前寫數據給瀏覽器
若是調用forward方法以前向Servlet引擎的緩衝區中寫入了內容,只要寫入到緩衝區中的內容尚未被真正輸出到客戶端,forward方法就能夠被正常執行,原來寫入到緩衝區中的內容將被清空,可是已寫入到HttpServletResponse對象中的響應頭字段信息保持有效
前面學習的 Request 對象能夠幫助咱們獲取到瀏覽器發過來的請求,想對應的,咱們就須要學習表明響應的 response 對象,它能夠幫助咱們進行對客戶端的響應工做
response做爲響應對象,他提供了兩個響應流對象,能夠向客戶端輸出響應正文
//獲取字符流 l PrintWriter out = response.getWriter() //獲取字節流 l ServletOutputStream out = response.getOutputStream()
ServletOutputStream servletOutputStream = resp.getOutputStream(); servletOutputStream.write("你好世界".getBytes()); servletOutputStream.write("Just for test".getBytes());
若是Tomcat版本在8如下 在outputStream中使用print()方法接收字符串,因爲編碼的問題,輸出中文字符串的時候,就會出現亂碼問題
緣由是,outputStream是輸出二進制的數據,print()方法先有一個將字符串轉爲二進制的過程,Tomcat會使用IOS 8859-1編碼轉換,因此出現了問題
可是使用write()卻能夠很好的解決這個問題,這是由於,write("Just for test".getBytes());
轉換爲byte[]數組的時候默認使用的是gb2312編碼,因此不會出現問題
可是爲了後續方便,咱們仍是要使用UFT-8編碼,若是咱們在上一步驟中指定編碼,看看如何
response.getOutputStream.write("你好世界".getBytes().getBytes("UTF-8"));
結果就是會出現亂碼,這是由於客戶端瀏覽器不知道響應數據是什麼編碼的,那麼如何解決這個問題呢
解決方案:
A:設置消息頭
//設置頭信息,告訴瀏覽器我回送的數據是UTF-8的 response.setHeader("Content-Type","text/html;charset=UTF-8"); response.getOutputStream.write("你好世界".getBytes().getBytes("UTF-8"));
B:使用html標籤模擬一個http消息頭
ServletOutputStream servletOutputStream = resp.getOutputStream(); //使用meta標籤模擬http消息頭,告訴瀏覽器回送數據的編碼和格式 servletOutputStream.write("<meta http-equiv='content-type' content='text/html;charset=UTF-8>".getBytes()); servletOutputStream.write("你好世界".getBytes().getBytes("UTF-8"));
C:推薦方法
//設置瀏覽器用UTF-8編碼顯示數據 resp.setContentType("text/html;charset=UTF-8"); //獲取到printWriter對象 PrintWriter printWriter = resp.getWriter(); printWriter.writer("你好世界")
好處:不僅會調用response.setCharaceterEncoding(「utf-8」)
,還會設置content-type響應頭(客戶端瀏覽器會使用content-type頭來解讀響應數據)
總結:響應正文內容爲字符,那麼使用respone.getWriter()
,若是響應內容是字節,例以下載文件,可使用 response.getOutputStream()
注意:在同一個請求中,不能同時使用這兩個流,不然會拋出 IllegalStateException 異常
getWriter() 的緩衝區問題
它的類型是PrintWriter類型的,因此它有緩衝區,緩衝區的默認大小爲8KB,在限定代銷範圍之內,數據先存放在緩衝區,等到超過範圍後,服務器刷新流,緩衝區中的數據發送倒客戶端,若是想要響應數據立刻發送到客戶端,能夠調用response.flushBuffer()方法來手動刷新緩衝區
使用 response 對象的 setHeader() 方法來設置響應頭
//設置content-type響應頭,告訴瀏覽器響應內容爲html類型,編碼爲utf-8。並且同時會設置response的字符流編碼爲utf-8,即response.setCharaceterEncoding(「uaav tf-8」); response.setHeader(「content-type」, 「text/html;charset=utf-8」) //5秒後自動跳轉到指定主頁 response.setHeader("Refresh","5; URL=http://www.xxx.com"):
//設置狀態碼 response.setStatus(200) //當發送錯誤狀態碼時,跳轉到指定錯誤頁面,但能夠顯示錯誤信息 response.sendError(404, 「您要查找的資源不存在」)
//等同於response.setHeader(「content-type」, 「text/html;charset=utf-8」) response.setContentType("text/html;charset=utf-8") //設置字符響應流的字符編碼爲UTF-8 response.setCharacterEncoding(「utf-8」) //下例表示定時刷新,3秒後跳轉頁面 response.setHeader("Refresh", "3;URL=Bservlet");
當你訪問 www.xxx.com
的時候,頁面被跳轉到了另外一個頁面,而且瀏覽器地址欄中的URL也發生了變化,這種技術就叫作重定向
完成重定向有兩個關鍵的地方
響應碼200的意思是響應成功,而重定向對應的響應碼爲302,因此咱們須要設置響應碼
由於重定向的原理爲,發出二次請求,因此你須要給瀏覽器指定第二次請求的URL,因此須要蛇者Location頭
注意:同服務器下可使用相對路徑
response.setStatus(302); response.setHeader("Location", "www.xxx.com");
簡單的寫法
response.sendRedirect("www.xxx.com");
(一) 實際發生未知不一樣,地址欄不一樣
A:轉發是發生在服務器的
B:轉發是由服務器進行跳轉的,轉發時,瀏覽器的地址欄是沒有發生變化的,(訪 問了Servlet1後即便頁面跳轉到了Servlet2,但瀏覽器的地址仍是Servlet1的) 也就是說瀏覽器是不知道該跳轉的動做,實現轉發只是一次的http請求,一次轉 發中request和response對象都是同一個,這也解釋了爲何可使用request 做爲域對象進行Servlet之間的通信
C:重定向是發生在瀏覽器的
D:重定向是由瀏覽器進行跳轉的,進行重定向跳轉的時候,瀏覽器的地址會發生變化,實現重定向的原理是由response的狀態碼和location頭組合而實現的,這 是由瀏覽器進行的頁面跳轉實現會發出兩個http請求,request域對象是無效的, 由於它不是同一個request對象
(二) 用法不一樣
原則:給服務器用的直接從資源名開始寫,給瀏覽器用的要把應用名協寫上
Requst.getRequestDispatcher(「/資源名 URL」).forward(request,response);
轉發時「/」表明的是本應用程序的根目錄(web-01)
Response.send(「/web應用/資源名URL」);
重定向時「/」表明的是webapps目錄
(三) 可以去往的URL的範圍不一樣
轉發是服務器跳轉,只能去往當前web應用的資源
重定向是服務器跳轉,能夠去往任何的資源
(四) 傳遞數據的類型不一樣
轉發的request對象能夠傳遞各類類型的數據,包括對象
重定向只能傳遞字符串
(五) 跳轉的時間不一樣
轉發時:執行到跳轉語句就會馬上跳轉
重定向:整個頁面執行完後纔會執行跳轉
總結:轉發是帶着轉發前的請求的參數。重定向時新的請求
典型的應用場景:
1:轉發:訪問Servlet處理業務邏輯,而後轉發到jsp中去處理結果,瀏覽器裏URL不變
2:重定向:提交表單,處理成功後重定向到另外一個jsp,防止表單重複提交,瀏覽器裏的URL變了
若是內容中有什麼不足,或者錯誤的地方,歡迎你們給我留言提出意見, 蟹蟹你們 !^_^
若是能幫到你的話,那就來關注我吧!(系列文章均會在公衆號第一時間更新)
在這裏的咱們素不相識,卻都在爲了本身的夢而努力 ❤一個堅持推送原創Java技術的公衆號:理想二旬不止