Web 的請求HttpServletrequest和響應HttpServletResponse


HttpServletrequest 與HttpServletResponse總結

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

由來

  Web服務器收到一個http請求,會針對每個請求創建一個HttpServletRequestHttpServletResponse對象,向客戶端發送數據找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 中支持的方法包括,GETPOSTHEADOPTIONS

PUTDELETE 和 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指定的
getParameterValuesString 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)瀏覽器會把字節數組轉換成字符

  1. 系統默認的編碼方式爲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狀態碼和管理CookieHttpServletResponse對象代表服務器的響應。這個對象中封裝了向客戶端發送數據、發送響應頭,發送響應狀態碼的方法

HttpServletResponse對象可以向客戶端發送三種類型的數據:

a.響應頭(Response headers)

b.狀態碼(ProtocolStatus codeDescription)

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中直接瀏覽驗證碼是亂碼,放在<img>裏面則不會

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

response.setHeader(Refresh,2;url=http://www.baidu.com); 頁面的刷新

消息實體內容 通過輸出流對象進行設置,用以下兩個方法:

Response.getOutputStream()  字節輸出流對象

Response.getWriter()   字符的輸出流對象

 getOutputStreamgetWriter方法的比較

1getOutputStream方法用於返回Servlet引擎創建的字節輸出流對象,Servlet程序可以按字節形式輸出響應正文。

2getWriter方法用於返回Servlet引擎創建的字符輸出流對象,Servlet程序可以按字符形式輸出響應正文。

3getOutputStreamgetWriter這兩個方法互相排斥,調用了其中的任何一個方法後,就不能再調用另一方法。要不會出現錯誤java.lang.IllegalStateException

4getOutputStream方法返回的是字節輸出流對象的類型爲ServletOutputStream,它可以直接輸出字節數組中的二進制數據。

5getWriter方法將Servlet引擎的數據緩衝區包裝成PrintWriter類型的字符輸出流對象後返回,PrintWriter對象可以直接輸出字符文本內容。

6Servlet程序向ServletOutputStreamPrintWriter對象中寫入的數據將被Servlet引擎獲取,Servlet引擎將這些數據當作響應消息的正文,然後再與響應狀態行和各響應頭組合後輸出到客戶端。

7Serlvetservice方法結束後,Servlet引擎將檢查getWritergetOutputStream方法返回的輸出流對象是否已經調用過close方法,如果沒有,Servlet引擎將調用close方法關閉該輸出流對象。

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

選擇getOutputStreamgetWrite方法的要點

1PrintWriter對象輸出字符文本內容時,它內部還是將字符串轉換成了某種字符集編碼的字節數組後再進行輸出,使用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);

         }

         in.close();

     }

案例2

@RequestMapping("/download")

public void download(HttpServletRequest req,HttpServletResponse res){

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

  //1.獲取要下載的文件的絕對路徑

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

File file=new File(realPath+"/"+fileName); //設置content-disposition響應頭控制瀏覽器以下載的形式打開文件

        res.setCharacterEncoding("utf-8");

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

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

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

        OutputStream out = res.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$()取值可以在input標籤的value中使用$()取值

ServletRequest與ServletResponse

ServletRequest

代表一個HTTP請求,請求在內存中是一個對象,這個對象是一個容器,可以存放請求參數和屬性。

請求對象何時被創建,當通過URL訪問一個JSP或者Servlet的時候,也就是當調用Servletservice()doPut()doPost()doXxx()方法時候的時候,執行Servletweb服務器就自動創建一個ServletRequestServletResponse的對象,傳遞給服務方法作爲參數。

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

ServletResponse

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

相應對象有以下功能:

1、向客戶端寫入Cookie

2、重寫URL

3、獲取輸出流對象,向客戶端寫入文本或者二進制數據

4、設置響應客戶端瀏覽器的字符編碼類型

5、設置客戶端瀏覽器的MIME類型。

一個簡單的 servlet 容器

Servlet 接口需要實現下面的 個方法:

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.輸出緩衝區的介紹

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

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

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

4)如果在提交響應到客戶端時,輸出緩衝區中裝入的內容只是全部響應內容的一部分,Servlet引擎將使用HTTP1.1chunked編碼方式(通過設置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");

      }

技術改變世界

發佈了24 篇原創文章 · 獲贊 3 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章