Servlet技術——request、respone詳解

Servlet之request、respone詳解

Request

(一) 概述

request是Servlet.service()方法的一個參數,在客戶端發出每一個請求時,服務器都會建立一個request對象,並把請求數據封裝到request中,而後在調用Servlet.service()方法時傳遞給service()方法html

HttpServletRequest對象表明客戶端的請求,當客戶端經過HTTP協議訪問服務器時,HTTP請求頭中的全部信息都封裝在這個對象中,開發人員經過這個對象的方法,能夠得到客戶這些信息java

(二) 經常使用方法

(1) 域方法

存儲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)

(2) 獲取請求頭數據

//獲取指定名稱的請求頭
String getHeader(String name)
 
//獲取全部請求頭名稱
Enumeration getHeaderNames()
    
//獲取值爲int類型的請求頭
int getIntHeader(String name)

(3) 獲取請求相關的其餘方法

//獲取請求體的字節數,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()

爲了方便記憶,咱們畫一張圖輔助記憶安全

(4) 案例練習

案例一:防盜鏈服務器

顧名思義,就是說讓用戶只能在咱們站內訪問對應網頁,而經過複製連接到地址欄以及貼連接到別人的網站進行盜鏈,則所有跳轉回本身的連接頁面app

注意:有一部分響應代碼未接觸,可先照着敲,大體體會,後期回來看webapp

先看一下效果:jsp

這是咱們所制定的網站,簡單理解爲官網

在官網中,正常點擊連接訪問,頁面跳轉正常

若是咱們本地寫一個頁面,直接繞過 a.html 去訪問 http://localhost:8080/web-001/ServletDemo3" 此時頁面就會跳轉回a.html中去,也就會回到了咱們的官網,而且控制檯輸出:非法盜鏈,已經跳回原頁面訪問!

下面是具體的代碼實現

  • 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>
  • ServletDemo3
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("是他就是他,是他就是他,咱們的英雄葫蘆娃!!!");
    }
}

(三) request獲取請求參數

(1) GET/POST請求的使用位置

  • 瀏覽器地址欄直接輸入:必定是GET請求
  • 超連接:必定是GET請求
  • 表單:能夠是GET,也能夠是POST

(2) GET/POST請求的區別

A:GET請求
  • 請求參數會在瀏覽器的地址欄中顯示,因此不安全
  • 請求參數長度限制長度在1K以內
  • GET請求沒有請求體,沒法經過request.setCharacterEncoding()來設置參數的編碼
B:POST請求
  • 請求參數不會顯示瀏覽器的地址欄,相對安全
  • 請求參數長度沒有限制

(3) 獲取請求參數的通用方式(Get/Post都可)

//根據參數名稱獲取參數值
String getParameter(String name)

//根據參數名稱獲取參數值的數組 
String[] getParameterValues(String name)

//獲取全部請求的參數名稱
Enumeration<String> getParameterNames()

//獲取全部參數的map集合
Map<String,String[]> getParameterMap()

(一)表單提交數據【經過post方式提交數據】

  • b.html
<!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>
  • RequestDemo3
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方式提交數據】

  • c.html
<!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>
  • RequestDemo4
//省略包
@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後咱們會學習重定向問題,到時候與轉發作區分對別,請留意這一部分

(六) Servlet之間的通信

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);
    }
  • ServletB
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對象中的響應頭字段信息保持有效

Respone

前面學習的 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()方法來手動刷新緩衝區

(二) 設置響應頭信息、狀態碼以及其餘

(1) 設置響應頭

使用 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"):

(2) 設置狀態碼

//設置狀態碼
response.setStatus(200)

//當發送錯誤狀態碼時,跳轉到指定錯誤頁面,但能夠顯示錯誤信息
response.sendError(404, 「您要查找的資源不存在」)

(3) 其餘

//等同於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也發生了變化,這種技術就叫作重定向

完成重定向有兩個關鍵的地方

  • 設置響應碼
  • 設置Location頭

響應碼200的意思是響應成功,而重定向對應的響應碼爲302,因此咱們須要設置響應碼

由於重定向的原理爲,發出二次請求,因此你須要給瀏覽器指定第二次請求的URL,因此須要蛇者Location頭

注意:同服務器下可使用相對路徑

response.setStatus(302);
response.setHeader("Location", "www.xxx.com");

簡單的寫法

response.sendRedirect("www.xxx.com");

(四) 轉發和重定向的區別與使用場景

(1) 區別

(一) 實際發生未知不一樣,地址欄不一樣

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對象能夠傳遞各類類型的數據,包括對象

重定向只能傳遞字符串

(五) 跳轉的時間不一樣

轉發時:執行到跳轉語句就會馬上跳轉

重定向:整個頁面執行完後纔會執行跳轉

(2) 應用場景

總結:轉發是帶着轉發前的請求的參數。重定向時新的請求

典型的應用場景:

1:轉發:訪問Servlet處理業務邏輯,而後轉發到jsp中去處理結果,瀏覽器裏URL不變

2:重定向:提交表單,處理成功後重定向到另外一個jsp,防止表單重複提交,瀏覽器裏的URL變了

結尾:

若是內容中有什麼不足,或者錯誤的地方,歡迎你們給我留言提出意見, 蟹蟹你們 !^_^

若是能幫到你的話,那就來關注我吧!(系列文章均會在公衆號第一時間更新)

在這裏的咱們素不相識,卻都在爲了本身的夢而努力 ❤

一個堅持推送原創Java技術的公衆號:理想二旬不止

相關文章
相關標籤/搜索