1. 程式人生 > >servlet-HttpServletRequest、HttpServletResponse

servlet-HttpServletRequest、HttpServletResponse

 * 如果說DOM是javascript與HTML的橋樑,那麼servlet就是前端與後端的橋樑,HttpServletRequest和HttpServletResponse就是之間的信使,好了,廢話不多說!

* Web伺服器收到一個http請求,會針對每個請求建立一個HttpServletRequest和HttpServletResponse物件,向客戶端傳送資料找HttpServletResponse,從客戶端取資料找HttpServletRequest.

  • HTTP 協議是基於請求-響應的協議,客戶端請求一個檔案,伺服器對該請求進行響應。HTTP 使用 TCP 協議,預設使用 80 埠。最初的 HTTP 協議版本是 HTTP/0.9,後被 HTTP/1.0 替代。目前使用的版本是 HTTP/1.1,

  • 在 HTTP 協議中,總是由主動建立連線、傳送 HTTP 請求的客戶端來初始化一個事務。伺服器不負責連線客戶端,或建立一個到客戶端的回撥連線(callback connection)。

  • HttpServletRequest
      公共介面類HttpServletRequest繼承自ServletRequest.客戶端瀏覽器發出的請求被封裝成為一個HttpServletRequest物件。所有的資訊包括請求的地址,請求的引數,提交的資料,上傳的檔案客戶端的ip甚至客戶端作業系統都包含在其內。

  • 一個 HTTP 請求包含以下三部分:

    • a.請求地址(URL)

    • b.請求頭(Request headers)

    • c.實體資料(Entity body)

  • 舉例如下

    • POST /examples/default.jsp HTTP/1.1

    • Accept: text/plain; text/html

    • Accept-Language: en-gb

    • Connection: Keep-Alive

    • Host: localhost

    • User-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows 98)

    • Content-Length: 33

    • Content-Type: application/x-www-form-urlencoded

    • Accept-Encoding: gzip, deflate

    • lastName=Franks&firstName=Michael

每個 HTTP 請求都會有一個請求方法,HTTP1.1 中支援的方法包括,GET、POST、HEAD、OPTIONS、PUT、DELETE 和 TRACE。網際網路應用中最常用的是 GET 和 POST。

  • URI 指明瞭請求資源的地址,通常是從網站更目錄開始計算的一個相對路徑,因此它總是以斜線 “/”開頭的。URL 實際上是 URI 的一種型別,請求頭(header)中包含了一些關於客戶端環境和請求實體(entity)的有用的資訊。例如,客戶端瀏覽器所使用的語言,請求實體資訊的長度等。每個請求頭使用 CRLF(回車換行符,“\r\n”)分隔。注意請求頭的格式:

  • 請求頭名+英文空格+請求頭值

常用方法

  • 1.獲得客戶機資訊

    • getRequestURL方法返回客戶端發出請求時的完整URL。

    • getRequestURI方法返回請求行中的資源名部分。

    • getQueryString 方法返回請求行中的引數部分。

    • getRemoteAddr方法返回發出請求的客戶機的IP地址

    • getRemoteHost方法返回發出請求的客戶機的完整主機名

    • getRemotePort方法返回客戶機所使用的網路埠號

    • getLocalAddr方法返回WEB伺服器的IP地址。

    • getLocalName方法返回WEB伺服器的主機名

    • getMethod得到客戶機請求方式

    • getServerPath()獲取請求的檔案的路徑

  • * 2.獲得客戶機請求頭*

    • getHeader(string name)方法 獲得請求頭資訊
    • getHeaders(String name)方法
    • getHeaderNames方法 獲得全部請求頭資訊
  • * 3. 獲得客戶機請求引數(客戶端提交的資料)*

    • getParameter(name)方法 獲取請求中的引數,該引數是由name指定的
    • getParameterValues(String name)方法 獲取指定名稱引數的所有值陣列。它適用於一個引數名對應多個值的情況。如頁面表單中的複選框,多選列表提交的值。

    • getParameterNames方法 返回一個包含請求訊息中的所有引數名的Enumeration物件。通過遍歷這個Enumeration物件,就可以獲取請求訊息中所有的引數名。

    • getCharacterEncoding() 返回請求的字元編碼方式

    • getAttributeNames()返回當前請求的所有屬性的名字集合賦值:setAttribute()

    • getAttribute(String name) 返回name指定的屬性值

    • getsession()返回和客戶端相關的session,如果沒有給客戶端分配session,則返回null

    • getParameterMap():返回一個儲存了請求訊息中的所有引數名和值的Map物件。Map物件的key是字串型別的引數名,value是這個引數所對應的Object型別的值陣列

    • RequestDispatcher.forward 方法的請求轉發過程結束後,瀏覽器位址列保持初始的URL地址不變。方法在伺服器端內部將請求轉發給另外一個資源,瀏覽器只知道發出了請求並得到了響應結果,並不知道在伺服器程式內部發生了轉發行為。

    • request.setCharacterEncoding(“utf-8”);設定獲取請求的字元編碼

    • getReader() 獲取請求體的資料流

    • getInputStream() 獲取請求的輸入流中的資料

    • 通過輸入輸出流獲取 :getInputStream() 和 getReader()

    • 在讀取的時候通過流物件.read()方法讀取


Eg:

StringBuffer receiveMessage = new StringBuffer();

Scanner scanner = new Scanner(request.getInputStream(), "GBK");

    while (scanner.hasNext()) {

receiveMessage.append(scanner.next());

}

scanner.close();

String json =receiveMessage.toString()

    JSONObject obj = new JSONObject(json);

openId = obj.get("openid").toString();

出現亂碼的原因和解決

  • 1. java程式中預設的是中文字元—-unicode

  • 2. 系統會把在java程式中的unicode字元按照某種字符集編碼的方式轉換成位元組陣列,再通過瀏覽器輸出,瀏覽器在輸出的時候要進行解碼,只有在這兩種方式一樣的情況下,才不會出現亂碼。

    • 注:(1)某種字元編碼是用reponse物件去設定的,而且必須是在out.println之前使用,要不會出現錯誤,會拋找不到設定的字元編碼而出錯。

         設定編碼的兩種方式:

        response.setContentType("text/html;charset=utf-8");

        request.setCharacterEncoding("utf-8");
* (2)瀏覽器會把位元組陣列轉換成字元

    * 系統預設的編碼方式為ISO8859-1,如果沒有指定字元編碼,則輸出的都是亂碼,而且ISO8859-1不支援中文,所以不管瀏覽器在解碼的時候用的是什麼字符集編碼,在瀏覽器上的都是亂碼。

* 解決辦法如下

Post方式提交出現亂碼

  • request.setCharacterEncoding(“UTF-8”);

    • 請求中之所以會產生亂碼,就是因為伺服器和客戶端溝通的編碼不一致造成的,因此解決的辦法是:在客戶端和伺服器之間設定一個統一的編碼,之後就按照此編碼進行資料的傳輸和接收。

        由於客戶端是以UTF-8字元編碼將表單資料傳輸到伺服器端的,因此伺服器也需要設定以UTF-8字元編碼進行接收,要想完成此操作,伺服器可以直接使用從ServletRequest介面繼承而來的“setCharacterEncoding(charset)”方法進行統一的編碼設定。使用request.setCharacterEncoding(“UTF-8”);設定伺服器以UTF-8的編碼接收資料後,此時就不會產生中文亂碼問題了

Get方式提交出現亂碼

  • 對於以get方式傳輸的資料,request即使設定了以指定的編碼接收資料也是無效的,預設的還是使用ISO8859-1這個字元編碼來接收資料,客戶端以UTF-8的編碼傳輸資料到伺服器端,而伺服器端的request物件使用的是ISO8859-1這個字元編碼來接收資料,伺服器和客戶端溝通的編碼不一致因此才會產生中文亂碼的。

    • 解決辦法:在接收到資料後,先獲取request物件以ISO8859-1字元編碼接收到的原始資料的位元組陣列,然後通過位元組陣列以指定的編碼構建字串,解決亂碼問題。程式碼如下:

    • String name = request.getParameter(“name”);//接收資料

    • name =new String(name.getBytes(“ISO8859-1”), “UTF-8”) ;//獲取request物件以ISO8859-1字元編碼接收到的原始資料的位元組陣列,然後通過位元組陣列以指定的編碼構建字串,解決亂碼問題

  • HttpServletResponse
      HttpServletResponse繼承了ServletResponse介面,並提供了與Http協議有關的方法,這些方法的主要功能是設定HTTP狀態碼和管理Cookie。HttpServletResponse物件代表伺服器的響應。這個物件中封裝了向客戶端傳送資料、傳送響應頭,傳送響應狀態碼的方法

    • HttpServletResponse物件可以向客戶端傳送三種類型的資料:

      • a.響應頭(Response headers)

      • b.狀態碼(Protocol—Status code—Description)

      • c.實體資料(Entity body )

舉例如下:

HTTP/1.1 200 OK

Server: Microsoft-IIS/4.0

Date: Mon, 5 Jan 2004 13:13:33 GMT

Content-Type: text/html

Last-Modified: Mon, 5 Jan 2004 13:13:12 GMT

Content-Length: 112

<html><head><title>HTTP Response Example</title></head>....</html>
  • 常用方法

    • addHeader(String name,String value) 將指定的名字和值加入到響應的頭資訊中

    • encodeURL(String url) 編碼指定的URL

    • sendError(int sc) 使用指定狀態碼傳送一個錯誤到客戶端

    • setDateHeader(String name,long date) 將給出的名字和日期設定響應的頭部

    • setHeader(String name,String value) 將給出的名字和值設定響應的頭部

    • setStatus(int sc) 給當前響應設定狀態碼

  • HttpServletResponse.sendRedirect 方法對瀏覽器的請求直接作出響應,響應的結果就是告訴瀏覽器去重新發出對另外一個URL的訪問請求;方法呼叫者與被呼叫者使用各自的request物件和response物件,它們屬於兩個獨立的訪問請求和響應過程。

    • response.setContentType(“text/html;charset=utf-8”);
  • setContentType(String ContentType) 設定響應的MIME型別 ,頁面的設定文字型別,獲取或設定輸出流的 HTTP MIME 型別。輸出流的 HTTP MIME 型別。預設值為“text/html”。

  • MIME型別就是設定某種副檔名的檔案用一種應用程式來開啟的方式型別,當該副檔名檔案被訪問的時候,瀏覽器會自動使用指定應用程式來開啟。多用於指定一些客戶端自定義的檔名,以及一些媒體檔案開啟方式。

  • 使用輸出流輸出一張圖片的時候,比如做驗證碼圖片的時候 如果在Firefox中直接瀏覽驗證碼是亂碼,放在裡面則不會

這時候就要事先指定Response.ContentType = “image/jpeg”;//設定MIME型別

訊息實體內容 通過輸出流物件進行設定,用以下兩個方法:

* Response.getOutputStream()  位元組輸出流物件

* Response.getWriter()   字元的輸出流物件
  • getOutputStream和getWriter方法的比較

    • (1)getOutputStream方法用於返回Servlet引擎建立的位元組輸出流物件,Servlet程式可以按位元組形式輸出響應正文。

    • (2)getWriter方法用於返回Servlet引擎建立的字元輸出流物件,Servlet程式可以按字元形式輸出響應正文。

    • (3)getOutputStream和getWriter這兩個方法互相排斥,呼叫了其中的任何一個方法後,就不能再呼叫另一方法。要不會出現錯誤java.lang.IllegalStateException

    • (4)getOutputStream方法返回的是位元組輸出流物件的型別為ServletOutputStream,它可以直接輸出位元組陣列中的二進位制資料。

    • (5)getWriter方法將Servlet引擎的資料緩衝區包裝成PrintWriter型別的字元輸出流物件後返回,PrintWriter物件可以直接輸出字元文字內容。

    • (6)Servlet程式向ServletOutputStream或PrintWriter物件中寫入的資料將被Servlet引擎獲取,Servlet引擎將這些資料當作響應訊息的正文,然後再與響應狀態行和各響應頭組合後輸出到客戶端。

    • (7)Serlvet的service方法結束後,Servlet引擎將檢查getWriter或getOutputStream方法返回的輸出流物件是否已經呼叫過close方法,如果沒有,Servlet引擎將呼叫close方法關閉該輸出流物件。

    • 注:out.close();系統會自己釋放,但一般寫上

選擇getOutputStream和getWrite方法的要點

  • (1)PrintWriter物件輸出字元文字內容時,它內部還是將字串轉換成了某種字符集編碼的位元組陣列後再進行輸出,使用PrintWriter物件的好處就是不用程式設計人員自己來完成字串到位元組陣列的轉換。

  • (2)使用ServletOutputStream物件也能輸出內容全為文字字元的網頁文件,但是,如果網頁文件內容是在Servlet程式內部使用文字字串動態拼湊和創建出來的,則需要先將字元文字轉換成位元組陣列後輸出。

  • (3)如果一個網頁文件內容全部為字元文字,但是這些內容可以直接從一個位元組輸入流中讀取出來,然後再原封不動地輸出到客戶端,那麼就應該使用ServletOutputStream物件直接進行輸出,而不要使用PrintWriter物件進行輸出。

向客戶端寫入中文

  • 使用OutputStream向客戶端寫入中文:

  • String data = “中國”;

OutputStream stream = response.getOutputStream();//獲取一個向Response物件寫入資料的流,當tomcat伺服器進行響應的時候,會將Response中的資料寫給瀏覽器

stream.write(data.getBytes("UTF-8"));//此時在html頁面會出現亂碼,這是因為:伺服器將"中國"按照UTF-8碼錶進行編碼,得到對應的碼值假設是98,99,伺服器將碼值傳送給瀏覽器.瀏覽器預設按照GB2312進行解碼,在GB2312碼錶中對應的字元已不是"中國"

正確程式碼如下:

response.setHeader("Content-type","text/html;charset=UTF-8");//向瀏覽器傳送一個響應頭,設定瀏覽器的解碼方式為UTF-8

 String data = "中國";

 OutputStream stream = response.getOutputStream();

stream.write(data.getBytes("UTF-8"));

使用PrintWriter向客戶端寫入中文:

PrintWriter writer = response.getWriter();

writer.write("中國");

//同樣會出現亂碼,這是因為我們將"中國"寫入response物件時,tomcat伺服器為了將資料通過網路傳輸給瀏覽器,必須進行編碼,由於沒有指定編碼方式,預設採用ISO8859-1,當瀏覽器接收到資料後,根據GBK解碼必然出現亂碼

 正確程式碼如下:

response.setCharacterEncoding("UTF_8");//設定Response的編碼方式為UTF-8

response.setHeader("Content-type","text/html;charset=UTF-8");//向瀏覽器傳送一個響應頭,設定瀏覽器的解碼方式為UTF-8,其實設定了本句,也預設設定了Response的編碼方式為UTF-8,但是開發中最好兩句結合起來使用 ,設定響應頭,控制瀏覽器以指定的字元編碼編碼進行顯示,

 //response.setContentType("text/html;charset=UTF-8");同上句程式碼作用一樣

 PrintWriter writer = response.getWriter();

writer.write("中國");

  在獲取PrintWriter輸出流之前首先使用"response.setCharacterEncoding(charset)"設定字元以什麼樣的編碼輸出到瀏覽器,如:response.setCharacterEncoding("UTF-8");設定將字元以"UTF-8"編碼輸出到客戶端瀏覽器,然後再使用response.getWriter();獲取PrintWriter輸出流,這兩個步驟不能顛倒

使用Response實現檔案下載:

  • 檔案下載功能是web開發中經常使用到的功能,使用HttpServletResponse物件就可以實現檔案的下載

  • 檔案下載功能的實現思路:

    • 1.獲取要下載的檔案的絕對路徑

    • 2.獲取要下載的檔名

    • 3.設定content-disposition響應頭控制瀏覽器以下載的形式開啟檔案

    • 4.獲取要下載的檔案輸入流

    • 5.建立資料緩衝區//緩衝區解釋見下文

    • 6.通過response物件獲取OutputStream流

    • 7.將FileInputStream流寫入到buffer緩衝區

    • 8.使用OutputStream將緩衝區的資料輸出到客戶端瀏覽器

  • 案例1


private void downloadFileByOutputStream(HttpServletResponse response){

         //1.獲取要下載的檔案的絕對路徑

         String realPath = this.getServletContext().getRealPath("/download/1.JPG");

         //2.獲取要下載的檔名

         String fileName = realPath.substring(realPath.lastIndexOf("\\")+1);

         //3.設定content-disposition響應頭控制瀏覽器以下載的形式開啟檔案

         response.setHeader("content-disposition", "attachment;filename="+fileName);

         //4.根據檔案路徑獲取要下載的檔案輸入流

        InputStream in = new FileInputStream(realPath);

         int len = 0;

         //5.建立資料緩衝區

         byte[] buffer = new byte[1024];

         //6.通過response物件獲取OutputStream流

         OutputStream out = response.getOutputStream();

         //7.將FileInputStream流寫入到buffer緩衝區         
         while ((len = in.read(buffer)) > 0) {

         //8.使用OutputStream將緩衝區的資料輸出到客戶端瀏覽器

             out.write(buffer,0,len);

         }
         out.flush();
         out.close();
         in.close();

     }
  • 案例2

@RequestMapping("/download")

public void download(HttpServletRequest req,HttpServletResponse response){

String fileName = "plcdmb.xls";//要下載的檔名

    //1.獲取要下載的檔案的絕對路徑

String realPath = req.getSession().getServletContext().getRealPath("/wbms/download");

File file=new File(realPath+"/"+fileName); //設定content-disposition響應頭控制瀏覽器以下載的形式開啟檔案

        response.setCharacterEncoding("utf-8");

        response.setContentType("application/octet-stream");

        response.setHeader("Content-Disposition", "attachment;fileName="+ URLEncoder.encode("批量出單模板.xls", "UTF-8"));

        InputStream inputStream=new FileInputStream(file);根據路徑獲取要下載的檔案輸入流 

        OutputStream out = response.getOutputStream();//獲得輸出端輸出流

        byte[] b=new byte[1024];  //建立資料緩衝區

            int length;  

            while((length=inputStream.read(b))>0){  把檔案流寫到緩衝區裡

                out.write(b,0,length);  

            }  

            out.flush();

            out.close();

            inputStream.close();



}

在編寫下載檔案功能時,要使用OutputStream流,避免使用PrintWriter流,因為OutputStream流是位元組流,可以處理任意型別的資料,而PrintWriter流是字元流,只能處理字元資料,如果用字元流處理位元組資料,會導致資料丟失。


其他在jsp巢狀的java程式碼中執行js

<%

//儲存登入資訊

Cookie[] cookies=request.getCookies();//從request中獲得cookie物件的集合  

String phone="";//電話號

String state="";//

if(cookies!=null){  

    for(int i=0;i<cookies.length;i++){  

        if(cookies[i].getName().equals("state")){  

            state=cookies[i].getValue();

               if(state.equals("cont_failed")){

               out.write("<script language='javascript'> alert('hello'); </script>;");

               }                                                           

        }  

    }                                                                 

}

 %>

getWriter()輸出js程式碼的案例

1.res.getWriter().write("<script language=\"javascript\">location.href='"+req.getContextPath()+"/wbms/ecm//init.action';</script>");//在原頁面輸出

res.getWriter().flush();

2.response.getWriter().write("<script language='javascript'>alert('請上傳正確格式的檔案!!!');window.history.back();</script>");

3.res.getWriter().flush();
  • 如果不使用這種形式,傳值用request.setAttribute()來傳值跳轉用重定向或者轉發頁面取值可以用jstl的inputvalue使{}取值

  • ServletRequest與ServletResponse

  • ServletRequest

  • 代表一個HTTP請求,請求在記憶體中是一個物件,這個物件是一個容器,可以存放請求引數和屬性。

  • 請求物件何時被建立,當通過URL訪問一個JSP或者Servlet的時候,也就是當呼叫Servlet的service()、doGet()、doPost()、doXxx()方法時候的時候,執行Servlet的web伺服器就自動建立一個ServletRequest和ServletResponse的物件,傳遞給服務方法作為引數。

  • 請求物件由Servlet容器自動產生,這個物件中自動封裝了請求中get和post方式提交的引數,以及請求容器中的屬性值,還有http頭等等。當Servlet或者JSP得到這個請求物件的時候,就知道這個請求是從哪裡發出的,請求什麼資源,帶什麼引數等等。通過請求物件,可以獲得Session物件和客戶端的Cookie。請求需要指定URL,瀏覽器根據URL生成HTTP請求併發送給伺服器.

  • ServletResponse

  • 也是由容器自動建立的,代表Servlet對客戶端請求的響應,響應的內容一般是HTML,而HTML僅僅是響應內容的一部分。請求中如果還包含其他資源會依次獲取,如頁面中含有圖片,會進行第二個http請求用來獲得圖片內容。

  • 相應物件有以下功能:

    • 1、向客戶端寫入Cookie

    • 2、重寫URL

    • 3、獲取輸出流物件,向客戶端寫入文字或者二進位制資料

    • 4、設定響應客戶端瀏覽器的字元編碼型別

    • 5、設定客戶端瀏覽器的MIME型別。

  • 一個簡單的 servlet 容器 Servlet 介面需要實現下面的 5 個方法:


public void init(ServletConfig config) throws ServletException

public void service(ServletRequest request, ServletResponse response) throws ServletException,

java.io.IOException

public void destroy()

public ServletConfig getServletConfig()

public java.lang.String getServletInfo()
  • 在某個 servlet 類被例項化之後,init 方法由 servlet 容器呼叫。servlet 容器只調用該方法一次,呼叫後則可以執行服務方法了。在 servlet 接收任何請求之前,必須是經過正確初始化的。

  • 當一個客戶端請求到達後,servlet 容器就呼叫相應的 servlet 的 service 方法,並將 Request 和 Response物件作為引數傳入。在 servlet 例項的生命週期內,service 方法會被多次呼叫。

  • 在將 servlet 例項從服務中移除前,會呼叫 servlet 例項的 destroy 方法。一般情況下,在伺服器關閉前,會發生上述情況,servlet 容器會釋放記憶體。只有當 servlet 例項的 service 方法中所有的執行緒都退出或執行超時後,才會呼叫 destroy 方法。當容器呼叫了 destroy 方法後,就不會再呼叫 service 方法了。

  • 下面從 servlet 容器的角度觀察 servlet 的開發。在一個全功能 servlet 容器中,對 servlet 的每個 HTTP 請求來說,容器要做下面幾件事:

    • 1.當第一次呼叫 servlet 時,要載入 servlet 類,呼叫 init 方法(僅此一次);

    • 2.針對每個 request 請求,建立一個 Request 物件和一個 Resposne 物件;

    • 3.呼叫相應的 servlet 的 service 方法,將 Request 物件和 Response 物件作為引數傳入;

    • 4.當關閉 servlet 時,呼叫 destroy 方法,並解除安裝該 servlet 類。

  • 輸出緩衝區

  • 1.輸出緩衝區的介紹

    • (1)Servlet程式輸出的HTTP訊息的響應正文首先被寫入到Servlet引擎提供的一個輸出緩衝區中,直到輸出緩衝區被填滿或者Servlet程式已經寫入了所有的響應內容,緩衝區中的內容才會被Servlet引擎傳送到客戶端。

    • (2)使用輸出緩衝區後,Servlet引擎就可以將響應狀態行、各響應頭和響應正文嚴格按照HTTP訊息的位置順序進行調整後再輸出到客戶端。

    • (3)如果在提交響應到客戶端時,輸出緩衝區中已經裝入了所有的響應內容,Servlet引擎將計算響應正文部分的大小並自動設定Content-Length頭欄位。

(4)如果在提交響應到客戶端時,輸出緩衝區中裝入的內容只是全部響應內容的一部分,Servlet引擎將使用HTTP1.1的chunked編碼方式(通過設定Transfer-Encoding頭欄位來指定)傳輸響應內容。


輸出緩衝區的有關方法  

      System.out.println(response.getBufferSize());    //讀取快取區的大小

      response.setBufferSize(1024);  //設定緩衝區的的大小,會小與你設定的值

      System.out.println(response.getBufferSize());

      int len=response.getBufferSize();  //填滿緩衝區

      for(int i =0;i<len;i++){

         System.out.println("w");

      }