Servlet第四篇【request對象經常使用方法、應用】

什麼是HttpServletRequest

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

簡單來講,要獲得瀏覽器信息,就找HttpServletRequest對象html

HttpServletRequest經常使用方法

得到客戶機【瀏覽器】信息

  • getRequestURL方法返回客戶端發出請求時的完整URL。
  • getRequestURI方法返回請求行中的資源名部分。
  • getQueryString 方法返回請求行中的參數部分。
  • getPathInfo方法返回請求URL中的額外路徑信息。額外路徑信息是請求URL中的位於Servlet的路徑以後和查詢參數以前的內容,它以「/」開頭。
  • getRemoteAddr方法返回發出請求的客戶機的IP地址
  • getRemoteHost方法返回發出請求的客戶機的完整主機名
  • getRemotePort方法返回客戶機所使用的網絡端口號
  • getLocalAddr方法返回WEB服務器的IP地址。
  • getLocalName方法返回WEB服務器的主機名

得到客戶機請求頭

  • getHeader方法
  • getHeaders方法
  • getHeaderNames方法

得到客戶機請求參數(客戶端提交的數據)

  • getParameter方法
  • getParameterValues(String name)方法
  • getParameterNames方法
  • getParameterMap方法

HttpServletRequest應用

防盜鏈

什麼是防盜鏈呢?好比:我如今有海賊王最新的資源,想要看海賊王的要在個人網頁上看。如今別的網站的人看到我有海賊王的資源,想要把個人資源粘貼在他本身的網站上。這樣我獨家的資源就被一個CTRL+C和CTRL+V搶走了?而反盜鏈就是不能被他們CRTL+C和CRTL+Vjava

  • 下面我模擬一下場景。如今我首頁先有一個超連接,指向着海賊王最新資源

  • 當我點進去,就能看到海賊王最新資源了

  • 其餘的人能夠經過複製粘貼個人地址,放到它們的網頁上

  • 這樣我就划不來啦【個人廣告你來沒看呢!】。想要看個人資源,就必須通過個人首頁點進去看
  • 想要實現這樣的效果,就要獲取Referer這個消息頭判斷Referer是否是從個人首頁來的。若是不是從個人首頁來的,跳轉回個人首頁
//獲取到網頁是從哪裏來的
        String referer = request.getHeader("Referer");

        //若是不是從個人首頁來或者從地址欄直接訪問的,
        if ( referer == null || !referer.contains("localhost:8080/zhongfucheng/index.jsp") ) {

            //回到首頁去
            response.sendRedirect("/zhongfucheng/index.jsp");
            return;
        }

        //能執行下面的語句,說明是從個人首頁點擊進來的,那沒問題,照常顯示
        response.setContentType("text/html;charset=UTF-8");
        response.getWriter().write("路飛作了XXXXxxxxxxxxxxxxxxxx");
  • 首先按正常預想的,別人從首頁點擊個人資源,訪問我海賊王最新的資源

\web

  • 可以成功訪問到資源

  • 若是我在瀏覽器直接輸入地址【此時Referer是爲null的】,咱們來看看

  • 跳回到首頁上,不能訪問到海賊王資源

  • 再試試,若是別人粘貼了個人資源url,在它的網頁上掛了一個網址呢。

  • 在別人網頁上點擊的時候

  • 又跳回到了個人首頁了。


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

<form action="/zhongfucheng/Servlet111" 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="男">男
                <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="飛翔">飛翔
            </td>
        </tr>
        <input type="hidden" name="aaa" value="my name is zhongfucheng">
        <tr>
            <td>你的來自於哪裏</td>
            <td>
                <select name="address">
                    <option value="廣州">廣州</option>
                    <option value="深圳">深圳</option>
                    <option value="北京">北京</option>
                </select>
            </td>
        </tr>
        <tr>
            <td>詳細說明:</td>
            <td>
                <textarea cols="30" rows="2" name="textarea"></textarea>
            </td>
        </tr>
        <tr>
            <td><input type="submit" value="提交"></td>
            <td><input type="reset" value="重置"></td>
        </tr>
    </table>
  • 在Servlet111中獲取到提交的數據,代碼以下
//設置request字符編碼的格式
        request.setCharacterEncoding("UTF-8");

        //經過html的name屬性,獲取到值
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String gender = request.getParameter("gender");

        //複選框和下拉框有多個值,獲取到多個值
        String[] hobbies = request.getParameterValues("hobbies");
        String[] address = request.getParameterValues("address");

        //獲取到文本域的值
        String description = request.getParameter("textarea");

        //獲得隱藏域的值
        String hiddenValue = request.getParameter("aaa");

        ....各類System.out.println().......
  • 向表單輸入數據

  • Servlet111獲得表單帶過來的數據,最後的一個數據是隱藏域帶過來的。


超連接方式提交數據

常見的get方式提交數據有使用超連接,sendRedirect()瀏覽器

格式以下:服務器

sendRedirect("servlet的地址?參數名="+參數值 &"參數名="+參數值);
  • 咱們來使用一下,經過超連接將數據帶給瀏覽器
<a href="/zhongfucheng/Servlet111?username=xxx">使用超連接將數據帶給瀏覽器</a>
  • 在Servlet111接收數據
//接收以username爲參數名帶過來的值
        String username = request.getParameter("username");
        System.out.println(username);
  • 注意看瀏覽器左下角

  • 服務器成功接收到瀏覽器發送過來的數據

  • 而且,傳輸數據明文的出如今瀏覽器的地址欄上

  • sendRedirect()和超連接相似,在這裏就不贅述了

解決中文亂碼問題

細心的朋友會發現,我在獲取表單數據的時候,有這句代碼request.setCharacterEncoding("UTF-8");,若是沒有這句代碼,會發生什麼事呢?咱們看看。微信

  • 再從新填寫數據

  • 在服務器查看提交過來的數據,全部的中文數據都亂碼了

  • 來這裏咱們來分析一下亂碼的緣由,在前面的博客中我已經介紹了,Tomcat服務器默認編碼是ISO 8859-1,而瀏覽器使用的是UTF-8編碼。瀏覽器的中文數據提交給服務器,Tomcat以ISO 8859-1編碼對中文編碼,當我在Servlet讀取數據的時候,拿到的固然是亂碼。而我設置request的編碼爲UTF-8,亂碼就解決了。
  • 接下來使用get方式傳遞中文數據,把表單的方式改爲get便可
  • 當咱們訪問的時候,又出現亂碼了!

  • 因而我按照上面的方式,把request對象設置編碼爲UTF-8試試
request.setCharacterEncoding("UTF-8");
        String name = request.getParameter("name");
  • 結果仍是亂碼。這是爲何呢?我明明已經把編碼設置成UTF-8了,按照post方式,亂碼問題已經解決了!。咱們來看看get和post方式的區別在哪?爲何post方式設置了request編碼就能夠解決亂碼問題,而get方式不能呢。
  • 首先咱們來看一下post方法是怎麼進行參數傳遞的。當咱們點擊提交按鈕的時候,數據封裝進了Form Data中http請求中把實體主體帶過去了【傳輸的數據稱之爲實體主體】,既然request對象封裝了http請求,因此request對象能夠解析到發送過來的數據,因而只要把編碼設置成UTF-8就能夠解決亂碼問題了

  • 而get方式不一樣,它的數據是從消息行帶過去的,沒有封裝到request對象裏面,因此使用request設置編碼是無效的。

  • 要解決get方式亂碼問題也不難,咱們既然知道Tomcat默認的編碼是ISO 8859-1,那麼get方式由消息體帶過去給瀏覽器的時候確定是用ISO 8859-1編碼了
//此時獲得的數據已是被ISO 8859-1編碼後的字符串了,這個是亂碼
        String name = request.getParameter("username");

        //亂碼經過反向查ISO 8859-1獲得原始的數據
        byte[] bytes = name.getBytes("ISO8859-1");

        //經過原始的數據,設置正確的碼錶,構建字符串
        String value = new String(bytes, "UTF-8");

上面的代碼有些難理解,我畫張圖說明一下:網絡

  • 通過咱們手工轉換,再來訪問一下

  • 好的,成功解決掉亂碼問題了。
  • 除了手工轉換,get方式還能夠改Tomcat服務器的配置來解決亂碼,可是不推薦使用,這樣不靈活。
  • 咱們都知道Tomcat默認的編碼是ISO 8859-1,若是在Tomcat服務器的配置下改爲是UTF-8的編碼,那麼就解決服務器在解析數據的時候形成亂碼問題了
  • 在8080端口的Connector上加入URIEncoding="utf-8",設置Tomcat的訪問該端口時的編碼爲utf-8,從而解決亂碼,這種改法是固定使用UTF-8編碼的
<Connector port="8080" protocol="HTTP/1.1" 
               connectionTimeout="20000" 
               redirectPort="8443" URIEncoding="utf-8"/>
  • 設置了編碼後,沒有作任何手工轉換,成功拿到數據

app

  • 固然也有另外一種改服務器編碼的方式。設置Tomcat的訪問該端口時的編碼爲頁面的編碼,這種改法是隨着頁面的編碼而變
<Connector port="8080" protocol="HTTP/1.1" 
               connectionTimeout="20000" 
               redirectPort="8443" useBodyEncodingForURI="true" />
  • 設置編碼爲UTF-8
request.setCharacterEncoding("UTF-8");
        String name = request.getParameter("name");
  • 再次訪問

  • 手寫超連接若是附帶中文參數問題,要URL重寫,在JSP博客中會講到
  • 總結:webapp

    • post方式直接改request對象的編碼
    • get方式須要手工轉換編碼
    • get方式也能夠修改Tomcat服務器的編碼,不推薦,由於會太依賴服務器了!
    • 提交數據能用post就用post

實現轉發

以前講過使用response的sendRedirect()能夠實現重定向,作到的功能是頁面跳轉,使用request的getRequestDispatcher.forward(request,response)實現轉發,作到的功能也是頁面跳轉,他們有什麼區別呢?如今我先來講下轉發jsp

  • 代碼以下所示
//獲取到requestDispatcher對象,跳轉到index.jsp
        RequestDispatcher requestDispatcher = request.getRequestDispatcher("/index.jsp");

        //調用requestDispatcher對象的forward()實現轉發,傳入request和response方法
        requestDispatcher.forward(request, response);
  • 訪問Servlet111

  • 上面已經說了,能夠經過sendRedirect()重定向能夠在資源尾部添加參數提交數據給服務器。那麼轉發能不能提交數據給服務器呢?
  • 答案明顯是能夠的,而且使用這種方法很是頻繁
  • 在講ServletContext的時候,曾經說過Servlet之間能夠經過ServletContext實現通信,ServletContext也能稱之爲域對象。而request也能夠稱之爲域對象,只不過ServletContext的域是整個web應用,而request的域僅僅表明一次http請求
  • 下面咱們來使用request實現Servlet之間的通信,Servlet111代碼
//以username爲關鍵字存zhongfucheng值
        request.setAttribute("username", "zhongfucheng");

        //獲取到requestDispatcher對象
        RequestDispatcher requestDispatcher = request.getRequestDispatcher("/Servlet222");

        //調用requestDispatcher對象的forward()實現轉發,傳入request和response方法
        requestDispatcher.forward(request, response);
  • Servlet222代碼
//獲取到存進request對象的值
        String userName = (String) request.getAttribute("username");

        //在瀏覽器輸出該值
        response.getWriter().write("i am :"+userName);
  • 訪問Servlet111看下效果

  • 如上圖所示,Servlet222成功拿到了request對象在Servlet111存進的數據
  • 如今問題又來了,咱們能夠使用ServletContext和request實現Servlet之間的通信,那麼咱們用哪種呢?通常的原則:可使用request就儘量使用request。由於ServletContext表明着整個web應用,使用ServletContext會消耗大量的資源,而request對象會隨着請求的結束而結束,資源會被回收使用request域進行Servlet之間的通信在開發中是很是頻繁的

轉發的時序圖

請求轉發的細節

  • 若是在調用forward方法以前,在Servlet程序中寫入的部份內容已經被真正地傳送到了客戶端,forward方法將拋出IllegalStateException異常。 也就是說:不要在轉發以前寫數據給瀏覽器
  • 咱們來試試是否是真的會出現異常。
OutputStream outputStream = response.getOutputStream();
        outputStream.write("--------------------------------------------".getBytes());

        //關閉流,確保讓數據到瀏覽器中
        outputStream.close();
        
        //跳轉
        request.getRequestDispatcher("/Foot").forward(request, response);
  • 訪問的時候,看到瀏覽器能夠輸出數據,Tomcat後臺拋出了異常

  • 若是在調用forward方法以前向Servlet引擎的緩衝區中寫入了內容,只要寫入到緩衝區中的內容尚未被真正輸出到客戶端,forward方法就能夠被正常執行,原來寫入到輸出緩衝區中的內容將被清空,可是,已寫入到HttpServletResponse對象中的響應頭字段信息保持有效

轉發和重定向的區別

實際發生位置不一樣,地址欄不一樣

  • 轉發是發生在服務器的

    • 轉發是由服務器進行跳轉的,細心的朋友會發現,在轉發的時候,瀏覽器的地址欄是沒有發生變化的,在我訪問Servlet111的時候,即便跳轉到了Servlet222的頁面,瀏覽器的地址仍是Servlet111的。也就是說瀏覽器是不知道該跳轉的動做,轉發是對瀏覽器透明的。經過上面的轉發時序圖咱們也能夠發現,實現轉發只是一次的http請求一次轉發中request和response對象都是同一個。這也解釋了,爲何可使用request做爲域對象進行Servlet之間的通信。
  • 重定向是發生在瀏覽器的

    • 重定向是由瀏覽器進行跳轉的,進行重定向跳轉的時候,瀏覽器的地址會發生變化的。曾經介紹過:實現重定向的原理是由response的狀態碼和Location頭組合而實現的。這是由瀏覽器進行的頁面跳轉實現重定向會發出兩個http請求request域對象是無效的,由於它不是同一個request對象

用法不一樣

不少人都搞不清楚轉發和重定向的時候,資源地址究竟怎麼寫。有的時候要把應用名寫上,有的時候不用把應用名寫上。很容易把人搞暈。記住一個原則:給服務器用的直接從資源名開始寫,給瀏覽器用的要把應用名寫上

  • request.getRequestDispatcher("/資源名 URI").forward(request,response)

    • 轉發時"/"表明的是本應用程序的根目錄【zhongfucheng】
  • response.send("/web應用/資源名 URI");

    • 重定向時"/"表明的是webapps目錄

可以去往的URL的範圍不同

  • 轉發是服務器跳轉只能去往當前web應用的資源
  • 重定向是瀏覽器跳轉,能夠去往任何的資源

傳遞數據的類型不一樣

  • 轉發的request對象能夠傳遞各類類型的數據,包括對象
  • 重定向只能傳遞字符串

跳轉的時間不一樣

  • 轉發時:執行到跳轉語句時就會馬上跳轉
  • 重定向:整個頁面執行完以後才執行跳轉

轉發和重定向使用哪個?

根據上面說明了轉發和重定向的區別也能夠很容易歸納出來。轉發是帶着轉發前的請求的參數的。重定向是新的請求

典型的應用場景:

  1. 轉發: 訪問 Servlet 處理業務邏輯,而後 forward 到 jsp 顯示處理結果,瀏覽器裏 URL 不變
  2. 重定向: 提交表單,處理成功後 redirect 到另外一個 jsp,防止表單重複提交,瀏覽器裏 URL 變了

RequestDispatcher再說明

RequestDispatcher對象調用forward()能夠實現轉發上面已經說過了。RequestDispatcher還有另一個方法include(),該方法能夠實現包含,有什麼用呢?

咱們在寫網頁的時候,通常網頁的頭部和尾部是不須要改變的。若是咱們多個地方使用Servlet輸出網頭和網尾的話,須要把代碼從新寫一遍。而使用RequestDispatcher的include()方法就能夠實現包含網頭和網尾的效果了

  • 咱們來操做吧!如今我有網頭和網尾的Servlet

  • 使用Servlet111將網頭和網尾包含
request.getRequestDispatcher("/Head").include(request, response);

        response.getWriter().write("--------------------------------------------");


        request.getRequestDispatcher("/Foot").include(request, response);
  • 訪問一下Servlet111,成功把網頭和網尾包含了


若是文章有錯的地方歡迎指正,你們互相交流。習慣在微信看技術文章的同窗,能夠關注微信公衆號:Java3y
相關文章
相關標籤/搜索