今天在研究人人網(wǎng)的Ouath認(rèn)證時,里面使用了重定向,,突然想到servlet中重定向?qū)崿F(xiàn)的原理是什么,,只記得response.sendRedirect()可以實現(xiàn)吧URL重新發(fā)送到瀏覽器進行重定向,RequestDispatcher有forword和include方法可以實現(xiàn)服務(wù)器內(nèi)的重定向,底層的實現(xiàn)還真沒研究過,。時間比較晚了,,就google了一下,有幾篇講解記錄一下:
1. 壇子里面的精華:http://www./topic/557980,, 值得好好看看
2. 網(wǎng)上搜來的,,講的比較全,
一 問題:
在servlet/JSP編程學(xué)習(xí)中,發(fā)現(xiàn)有兩種方法可以實現(xiàn)服務(wù)端輸出重定向,,一種是通過forward方法(例如JSP中
的<jsp:forward
page=”O(jiān)therPage.jsp”/>),,另一種則是通過運用
javax.servlet.http.HttpServletResponse接口的sendRedirect方法(例如
response.sendRedirect(“OtherPage.jsp”);這兩種方法有什么區(qū)別和聯(lián)系呢?讓我們看下面的分析,。
二 分析:(1)<JSP:FORWORD>
該
方法是利用服務(wù)器端先將數(shù)據(jù)輸出到緩沖區(qū)的機制,在把緩沖區(qū)(buffer)的內(nèi)容發(fā)送到客戶端之前,原來的不發(fā)送,改為發(fā)送該頁面的內(nèi)容,如果
在<JSP:FORWORD>之前有很多輸出,前面的輸出已使緩沖區(qū)滿,將自動輸出到客戶端,那么該語句將不起作用,這一點應(yīng)該特別注意. 補充知識:輸出緩沖區(qū)
缺省情況下:服務(wù)端要輸出到客戶端的內(nèi)容,不直接寫到客戶端,而是先寫到一個輸出緩沖區(qū)中.只有在下面三中情況下,,才會把該緩沖區(qū)的內(nèi)容輸出到客戶端上:
1該JSP網(wǎng)頁已完成信息的輸出
2輸出緩沖區(qū)已滿
3JSP中調(diào)用了out.flush()或response.flushbuffer()
輸出緩沖區(qū)的大小可以用:<%@page
buffer="none"|"nkb"%>或response.setBufferSize()設(shè)置,如下:
1設(shè)置輸出緩沖區(qū)的大小為1KB。<%@page
buffer="1kb"%>或response.setBufferSize(1);
2設(shè)置輸出緩沖區(qū)的大小為0,,即不緩沖,。<%@page
buffer="none" %>或response.setBufferSize(0);
用response.getBufferSize()或out.getBufferSize()可取的輸出緩沖區(qū)的大小,單位為字節(jié).
用response.isCommitted()可檢查看服務(wù)端是否已將數(shù)據(jù)輸出到客戶端. 如果返回值是TRUE則已將數(shù)據(jù)輸出到客戶端,是FALSE則還沒有。 (2)response.sendRedirect(“OtherPage.jsp”)
該
方法通過修改HTTP協(xié)議的HEADER部分,對瀏覽器下達重定向指令的,,讓瀏覽器對在location中指定的URL提出請求,,使瀏覽器顯示重定向網(wǎng)頁
的內(nèi)容。該方法可以接受絕對的或相對的URLs,。如果傳遞到該方法的參數(shù)是一個相對的URL,,那么Web container在將它發(fā)送到客戶端前會把它
轉(zhuǎn)換成一個絕對的URL。如果地址是相對的,,沒有一個’/’,,那么Web container就認(rèn)為它是相對于當(dāng)前的請求URI的。
因為這個方法是通過修改HTTP協(xié)議的HEADER實現(xiàn)的重定義功能,,而下面的方法也能改變HTTP HEADER屬性,,他們的原理是一樣的.
<%
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
String newLocn="/index.html";
response.setHeader("Location",newLocn);
%> 補充知識:HTTP應(yīng)答頭
Web服務(wù)器響應(yīng)瀏覽器或其他客戶程序的請求時,其應(yīng)答一般由以下幾個部分組成:一個狀態(tài)行,,幾個應(yīng)答頭,,一個空行,內(nèi)容文檔,。下面是一個最簡單的應(yīng)答:
HTTP/1.1 200 OK
Content-Type: text/plain
Hello World
1設(shè)置狀態(tài)信息
狀
態(tài)行包含HTTP版本,、狀態(tài)代碼、與狀態(tài)代碼對應(yīng)的簡短說明信息,。在大多數(shù)情況下,,除了Content-Type之外的所有應(yīng)答頭都是可選的。
Servlet可以利用狀態(tài)代碼來實現(xiàn)許多功能,。例如,,可以把用戶重定向到另一個網(wǎng)站,,就像我們上邊所看到的那個例子。下面我們就通過這個機會具體討論各
種狀態(tài)代碼的含義以及利用這些代碼可以做些什么,。
如前所述,,HTTP應(yīng)答狀態(tài)行包含HTTP版本、狀態(tài)代碼和對應(yīng)的狀態(tài)信息,。由于狀態(tài)信息直接和狀態(tài)代碼相關(guān),,而HTTP版本又由服務(wù)器確定,因此需要Servlet設(shè)置的只有一個狀態(tài)代碼,。
先給出常見的HTTP 1.1狀態(tài)代碼以及它們對應(yīng)的狀態(tài)信息和含義,,具體的使用方法我們接下來再做詳細的介紹,。
100 Continue 初始的請求已經(jīng)接受,,客戶應(yīng)當(dāng)繼續(xù)發(fā)送請求的其余部分。(HTTP 1.1新)
101 Switching Protocols 服務(wù)器將遵從客戶的請求轉(zhuǎn)換到另外一種協(xié)議(HTTP 1.1新)
200 OK 一切正常,,對GET和POST請求的應(yīng)答文檔跟在后面,。如果不用setStatus
設(shè)置狀態(tài)代碼,Servlet默認(rèn)使用202狀態(tài)代碼,。
201 Created 服務(wù)器已經(jīng)創(chuàng)建了文檔,,Location頭給出了它的URL。
202 Accepted 已經(jīng)接受請求,,但處理尚未完成,。
203 Non-Authoritative Information 文檔已經(jīng)正常地返回,但一些應(yīng)答頭可能不正確,,因為使用的是文檔的拷貝(HTTP 1.1新),。
204 No Content 沒有新文檔,瀏覽器應(yīng)該繼續(xù)顯示原來的文檔,。如果用戶定期地刷新頁面,,而Servlet可以確定用戶文檔足夠新,這個狀態(tài)代碼是很有用的,。
205 Reset Content 沒有新的內(nèi)容,,但瀏覽器應(yīng)該重置它所顯示的內(nèi)容。用來強制瀏覽器清除表單輸入內(nèi)容(HTTP 1.1新),。
206 Partial Content 客戶發(fā)送了一個帶有Range頭的GET請求,,服務(wù)器完成了它(HTTP 1.1新)。
300 Multiple Choices 客戶請求的文檔可以在多個位置找到,,這些位置已經(jīng)在返回的文檔內(nèi)列出,。如果服務(wù)器要提出優(yōu)先選擇,則應(yīng)該在Location應(yīng)答頭指明,。
301 Moved Permanently 客戶請求的文檔在其他地方,,新的URL在Location頭中給出,,瀏覽器應(yīng)該自動地訪問新的URL。
302
Found 類似于301,,但新的URL應(yīng)該被視為臨時性的替代,,而不是永久性的。注意,,在HTTP1.0中對應(yīng)的狀態(tài)信息是“Moved
Temporatily”,,而HttpServletResponse中相應(yīng)的常量是SC_MOVED_TEMPORARILY,而不是
SC_FOUND,。
出現(xiàn)該狀態(tài)代碼時,,瀏覽器能夠自動訪問新的URL,因此它是一個很有用的狀態(tài)代碼,。為此,,Servlet提供了一個專用的方
法,即sendRedirect,。使用response.sendRedirect(url)比使用
response.setStatus(response.SC_MOVED_TEMPORARILY)和
response.setHeader("Location",url)更好,。這是因為:
首先,代碼更加簡潔,。
第二,,使用sendRedirect,Servlet會自動構(gòu)造一個包含新鏈接的頁面(用于那些不能自動重定向的老式瀏覽器),。
最后,,sendRedirect能夠處理相對URL,自動把它們轉(zhuǎn)換成絕對URL,。
注意這個狀態(tài)代碼有時候可以和301替換使用,。例如,如果瀏覽器錯誤地請求http://host/~user
(缺少了后面的斜杠),,有的服務(wù)器返回301,,有的則返回302。
嚴(yán)格地說,,我們只能假定只有當(dāng)原來的請求是GET時瀏覽器才會自動重定向,。請參見307。
303 See Other 類似于301/302,,不同之處在于,,如果原來的請求是POST,Location頭指定的重定向目標(biāo)文檔應(yīng)該通過GET提?。℉TTP 1.1新),。 304 Not Modified 客戶端有緩沖的文檔并發(fā)出了一個條件性的請求(一般是提供If-Modified-Since頭表示客戶只想比指定日期更新的文檔)。服務(wù)器告訴客戶,,原來緩沖的文檔還可以繼續(xù)使用,。
305 Use Proxy 客戶請求的文檔應(yīng)該通過Location頭所指明的代理服務(wù)器提?。℉TTP 1.1新)。
307
Temporary Redirect
和302(Found)相同,。許多瀏覽器會錯誤地響應(yīng)302應(yīng)答進行重定向,,即使原來的請求是POST,即使它實際上只能在POST請求的應(yīng)答是303時
才能重定向,。由于這個原因,,HTTP
1.1新增了307,以便更加清除地區(qū)分幾個狀態(tài)代碼:當(dāng)出現(xiàn)303應(yīng)答時,,瀏覽器可以跟隨重定向的GET和POST請求,;如果是307應(yīng)答,則瀏覽器只
能跟隨對GET請求的重定向,。
注意,,HttpServletResponse中沒有為該狀態(tài)代碼提供相應(yīng)的常量。(HTTP 1.1新)
400 Bad Request 請求出現(xiàn)語法錯誤,。
401 Unauthorized 客戶試圖未經(jīng)授權(quán)訪問受密碼保護的頁面,。應(yīng)答中會包含一個WWW-Authenticate頭,,瀏覽器據(jù)此顯示用戶名字/密碼對話框,,然后在填寫合適的Authorization頭后再次發(fā)出請求。
403 Forbidden 資源不可用,。服務(wù)器理解客戶的請求,,但拒絕處理它。通常由于服務(wù)器上文件或目錄的權(quán)限設(shè)置導(dǎo)致,。
404 Not Found 無法找到指定位置的資源,。這也是一個常用的應(yīng)答,HttpServletResponse專門提供了相應(yīng)的方法:sendError(message),。
405 Method Not Allowed 請求方法(GET,、POST、HEAD,、DELETE,、PUT、TRACE等)對指定的資源不適用,。(HTTP 1.1新)
406 Not Acceptable 指定的資源已經(jīng)找到,,但它的MIME類型和客戶在Accpet頭中所指定的不兼容(HTTP 1.1新)。
407 Proxy Authentication Required 類似于401,,表示客戶必須先經(jīng)過代理服務(wù)器的授權(quán),。(HTTP 1.1新)
408 Request Timeout 在服務(wù)器許可的等待時間內(nèi),客戶一直沒有發(fā)出任何請求,??蛻艨梢栽谝院笾貜?fù)同一請求,。(HTTP 1.1新)
409 Conflict 通常和PUT請求有關(guān)。由于請求和資源的當(dāng)前狀態(tài)相沖突,,因此請求不能成功,。(HTTP 1.1新)
410 Gone 所請求的文檔已經(jīng)不再可用,而且服務(wù)器不知道應(yīng)該重定向到哪一個地址,。它和404的不同在于,,返回407表示文檔永久地離開了指定的位置,而404表示由于未知的原因文檔不可用,。(HTTP 1.1新)
411 Length Required 服務(wù)器不能處理請求,,除非客戶發(fā)送一個Content-Length頭。(HTTP 1.1新)
412 Precondition Failed 請求頭中指定的一些前提條件失?。℉TTP 1.1新),。
413 Request Entity Too Large 目標(biāo)文檔的大小超過服務(wù)器當(dāng)前愿意處理的大小。如果服務(wù)器認(rèn)為自己能夠稍后再處理該請求,,則應(yīng)該提供一個Retry-After頭(HTTP 1.1新),。
414 Request URI Too Long URI太長(HTTP 1.1新)。
416 Requested Range Not Satisfiable 服務(wù)器不能滿足客戶在請求中指定的Range頭,。(HTTP 1.1新)
500 Internal Server Error 服務(wù)器遇到了意料不到的情況,,不能完成客戶的請求。
501 Not Implemented 服務(wù)器不支持實現(xiàn)請求所需要的功能,。例如,,客戶發(fā)出了一個服務(wù)器不支持的PUT請求。
502 Bad Gateway 服務(wù)器作為網(wǎng)關(guān)或者代理時,,為了完成請求訪問下一個服務(wù)器,,但該服務(wù)器返回了非法的應(yīng)答。
503 Service Unavailable 服務(wù)器由于維護或者負載過重未能應(yīng)答,。例如,,Servlet可能在數(shù)據(jù)庫連接池已滿的情況下返回503。服務(wù)器返回503時可以提供一個Retry-After頭,。
504 Gateway Timeout 由作為代理或網(wǎng)關(guān)的服務(wù)器使用,,表示不能及時地從遠程服務(wù)器獲得應(yīng)答。(HTTP 1.1新)
505 HTTP Version Not Supported 服務(wù)器不支持請求中所指明的HTTP版本,。(HTTP 1.1新)
如前所述,,HTTP應(yīng)答狀態(tài)行包含HTTP版本、狀態(tài)代碼和對應(yīng)的狀態(tài)信息,。由于狀態(tài)信息直接和狀態(tài)代碼相關(guān),,而HTTP版本又由服務(wù)器確定,因此需要Servlet設(shè)置的只有一個狀態(tài)代碼,。拿我們上邊的例子來看,。其中有一句就是設(shè)置HTTP應(yīng)答頭的狀態(tài)代碼的,,是:
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); Servlet
設(shè)置狀態(tài)代碼一般使用HttpServletResponse的setStatus方法。setStatus方法的參數(shù)是一個整數(shù)(即狀態(tài)代碼),,不過為
了使得代碼具有更好的可讀性,,可以用HttpServletResponse中定義的常量來避免直接使用整數(shù)。這些常量根據(jù)HTTP
1.1中的標(biāo)準(zhǔn)狀態(tài)信息命名,,所有的名字都加上了SC前綴(Status
Code的縮寫)并大寫,,同時把空格轉(zhuǎn)換成了下劃線。也就是說,與狀態(tài)代碼301對應(yīng)的狀態(tài)信息是“Moved
Permanently”,則HttpServletResponse中的對應(yīng)常量名字為SC_MOVED_PERMANENTLY,。但有兩個例外:和狀
態(tài)代碼302對應(yīng)的常量根據(jù)HTTP 1.0命名為SC_MOVED_TEMPORARILY,而不是SC_FOUND,,而307沒有對應(yīng)的常量。
雖然設(shè)置狀態(tài)代碼一般使用的是response.setStauts(int)方法,,但為了簡單起見,,HttpServletResponse為兩種常見
的情形提供了專用方法:sendError方法生成一個404應(yīng)答,同時生成一個簡短的HTML錯誤信息文檔,;sendRedirect方法生成一個
302應(yīng)答,,同時在Location頭中指示新文檔的URL。這種方法就是我們前邊提到的
response.sendRedirect(“OtherPage.jsp”)實現(xiàn)重定向的方法的原理,。它與301應(yīng)答,,既
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY)的區(qū)別請看上邊狀態(tài)列表的
紅色標(biāo)記部分,,這里不再重復(fù),。
2設(shè)置HTTP應(yīng)答頭
設(shè)置HTTP應(yīng)答頭往往和設(shè)置狀態(tài)行
中的狀態(tài)代碼結(jié)合起來。例如,,有好幾個表示“文檔位置已經(jīng)改變”的狀態(tài)代碼都伴隨著一個Location頭,,而401(Unauthorized)狀態(tài)代
碼則必須伴隨一個WWW-Authenticate頭。這些都在相應(yīng)的狀態(tài)代碼的狀態(tài)信息說明中都提到了,。
然而,,即使在沒有設(shè)置特殊含義的狀態(tài)代碼時,指定應(yīng)答頭也是很有用的,。應(yīng)答頭可以用來完成:設(shè)置Cookie,,指定修改日期,指示瀏覽器按照指定的間隔刷
新頁面,,聲明文檔的長度以便利用持久HTTP連接,,……等等許多其他任務(wù)。還拿我們上邊的例子來看,,其中有一句就是設(shè)置HTTP應(yīng)答頭的,,是:
response.setHeader("Location",newLocn);
設(shè)置應(yīng)答頭最常用的方法是HttpServletResponse的setHeader,,該方法有兩個參數(shù),分別表示應(yīng)答頭的名字和值,。和設(shè)置狀態(tài)代碼相似,,設(shè)置應(yīng)答頭應(yīng)該在發(fā)送任何文檔內(nèi)容之前進行。
應(yīng)答頭 說明
Allow 服務(wù)器支持哪些請求方法(如GET,、POST等),。
Content-Encoding
文檔的編碼(Encode)方法。只有在解碼之后才可以得到Content-Type頭指定的內(nèi)容類型,。利用gzip壓縮文檔能夠顯著地減少HTML文檔
的下載時間,。Java的GZIPOutputStream可以很方便地進行g(shù)zip壓縮,但只有Unix上的Netscape和Windows上的IE
4,、IE
5才支持它,。因此,Servlet應(yīng)該通過查看Accept-Encoding頭(即request.getHeader("Accept-
Encoding"))檢查瀏覽器是否支持gzip,,為支持gzip的瀏覽器返回經(jīng)gzip壓縮的HTML頁面,,為其他瀏覽器返回普通頁面。
Content-Length
表示內(nèi)容長度,。只有當(dāng)瀏覽器使用持久HTTP連接時才需要這個數(shù)據(jù),。如果你想要利用持久連接的優(yōu)勢,可以把輸出文檔寫入
ByteArrayOutputStram,,完成后查看其大小,,然后把該值放入Content-Length頭,最后通過
byteArrayStream.writeTo(response.getOutputStream()發(fā)送內(nèi)容,。
Content-Type
表示后面的文檔屬于什么MIME類型,。Servlet默認(rèn)為text/plain,但通常需要顯式地指定為text/html,。由于經(jīng)常要設(shè)置
Content-Type,,因此HttpServletResponse提供了一個專用的方法setContentTyep。
Date 當(dāng)前的GMT時間,。你可以用setDateHeader來設(shè)置這個頭以避免轉(zhuǎn)換時間格式的麻煩,。
Expires 應(yīng)該在什么時候認(rèn)為文檔已經(jīng)過期,從而不再緩存它,?
Last-Modified
文檔的最后改動時間,。客戶可以通過If-Modified-Since請求頭提供一個日期,,該請求將被視為一個條件GET,,只有改動時間遲于指定時間的文
檔才會返回,否則返回一個304(Not Modified)狀態(tài)。Last-Modified也可用setDateHeader方法來設(shè)置,。 Location 表示客戶應(yīng)當(dāng)?shù)侥睦锶ヌ崛∥臋n,。Location通常不是直接設(shè)置的,而是通過HttpServletResponse的sendRedirect方法,,該方法同時設(shè)置狀態(tài)代碼為302,。
Refresh 表示瀏覽器應(yīng)該在多少時間之后刷新文檔,以秒計,。除了刷新當(dāng)前文檔之外,,你還可以通過setHeader("Refresh", "5; URL=http://host/path")讓瀏覽器讀取指定的頁面。
注
意這種功能通常是通過設(shè)置HTML頁面HEAD區(qū)的<META HTTP-EQUIV="Refresh"
CONTENT="5;URL=http://host/path">實現(xiàn),,這是因為,,自動刷新或重定向?qū)τ谀切┎荒苁褂肅GI或Servlet的
HTML編寫者十分重要。但是,,對于Servlet來說,,直接設(shè)置Refresh頭更加方便。
注意Refresh的意義是“N秒之后刷新本頁面或訪問指定頁面”,,而不是“每隔N秒刷新本頁面或訪
問指定頁面”,。因此,連續(xù)刷新要求每次都發(fā)送一個Refresh頭,,而發(fā)送204狀態(tài)代碼則可以阻止瀏覽器繼續(xù)刷新,,不管是使用Refresh頭還是
<META HTTP-EQUIV="Refresh" ...>。
注意Refresh頭不屬于HTTP 1.1正式規(guī)范的一部分,,而是一個擴展,,但Netscape和IE都支持它。
Server 服務(wù)器名字,。Servlet一般不設(shè)置這個值,,而是由Web服務(wù)器自己設(shè)置。
Set-Cookie
設(shè)置和頁面關(guān)聯(lián)的Cookie,。Servlet不應(yīng)使用response.setHeader("Set-Cookie",
...),,而是應(yīng)使用HttpServletResponse提供的專用方法addCookie。參見下文有關(guān)Cookie設(shè)置的討論,。
WWW-Authenticate
客戶應(yīng)該在Authorization頭中提供什么類型的授權(quán)信息?在包含401(Unauthorized)狀態(tài)行的應(yīng)答中這個頭是必需的,。例
如,,response.setHeader("WWW-Authenticate", "BASIC realm=\"executives\"")。
注意Servlet一般不進行這方面的處理,,而是讓W(xué)eb服務(wù)器的專門機制來控制受密碼保護頁面的訪問(例如.htaccess),。
到此我們應(yīng)該可以理解用下面代碼實現(xiàn)重定向的原理:修改HTTP協(xié)議的HEADER部分,對瀏覽器下達重定向指令的,讓瀏覽器對在location中指定的URL提出請求,使瀏覽器顯示重定向網(wǎng)頁的內(nèi)容,。
<%
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); 設(shè)置HTTP應(yīng)答頭狀態(tài)代碼
String newLocn="/index.html";
response.setHeader("Location",newLocn); 設(shè)置HTTP應(yīng)答頭
%>
三 總結(jié):
通過上邊的分析,,我們清楚了這兩種服務(wù)器端重
定向方法的原理,<jsp:forward>是利用服務(wù)器端先將數(shù)據(jù)輸出到緩沖區(qū)的機制,,在把緩沖區(qū)的內(nèi)容發(fā)送到客戶端之前,,通過停止調(diào)用頁
的處理,將執(zhí)行轉(zhuǎn)向響應(yīng)的頁面,,從而實現(xiàn)的重定位功能,,原調(diào)用頁的輸出緩沖區(qū)中任何尚未在瀏覽器中顯示(刷新)的內(nèi)容將被清楚,不再顯示,。
而response.sendRedirect(“OtherPage.jsp”)是通過修改HTTP協(xié)議的HEADER部分,對瀏覽器下達重定向指令的,,讓瀏覽器對在location中指定的URL提出請求,使瀏覽器顯示重定向網(wǎng)頁的內(nèi)容的,。
四 對比:
1 forward方法是在Web container內(nèi)部工作的。SendRedirect方法需要到客戶端的一個往返,。所以forward方法要比sendreRirect要快
2 forward方法只能重定向到同一個Web應(yīng)用程序中的一個資源。而sendRedirect方法可以重定向到任何URL
3 forward方法還將原始的HTTP請求對象(request)轉(zhuǎn)到目錄頁,。而sendRedirect方法的功能是觸發(fā)瀏覽器轉(zhuǎn)向指定的URL,,并不會將原始請求對象轉(zhuǎn)發(fā)。
在例:C:\Tomcat 5.0\webapps\test\regiester10中我們可以看到在WELCOME,。JSP中有:
<tr><td>姓名:<%=request.getParameter("userName")%></td></tr>
當(dāng)在LOGIN.JSP中用response.sendRedirect("welcome.jsp");重定向到WELCOME.JSP時,,輸出為:
姓名:null
當(dāng)在LOGIN.JSP中用<jsp:forward page = "welcome.jsp"/>重定向到WELCOME.JSP時,輸出為:
姓名:wangjian
4.sendRedirect能夠處理相對URL,,自動把它們轉(zhuǎn)換成絕對URL,。
|