當構(gòu)建一個Web應(yīng)用邦蜜,把一個請求的處理轉(zhuǎn)給其它servlet,或者在response中包含其它servlet的輸出通常很有用處署辉。RequestDispatcher接口提供一種機制來完成這個功能押赊。
當在請求上啟動了異步叶堆,AsyncContext允許用戶把請求分發(fā)回servlet容器。
一摘昌、獲取一個RequestDispatcher
實現(xiàn)了RequestDispatcher接口的對象可以通過ServletContext中的下列方法被獲人傺:
- getRequestDispatcher
- getNamedDispatcher
getRequestDispatcher方法需要傳入一個字符串參數(shù),這個參數(shù)描述了ServletContext范圍內(nèi)的一個路徑聪黎。這個路勁必須相對ServletContext的根目錄罕容,并且以"/"開頭或者為空。這個方法根據(jù) Mapping Requests to Servlets 章節(jié)里servlet路徑匹配規(guī)則稿饰,使用這個路徑來查找一個servlet锦秒,使用一個RequestDispatcher 對象來包裝它,并且返回一個結(jié)果對象喉镰。如果基于給定的路徑?jīng)]有servlet能夠被解析旅择,提供它的RequestDispatcher返回那個路徑的內(nèi)容。
getNamedDispatcher方法需要傳入一個字符串參數(shù)侣姆,這個參數(shù)用來表示ServletContext已知的一個servlet的名字生真。如果一個servlet被發(fā)現(xiàn)脖咐,它會被用一個RequestDispatcher對象來包裝并且返回這個對象。如果沒有servlet與指定的名字關(guān)聯(lián)汇歹,方法必須返回null屁擅。
為了使RequestDispatcher對象能夠通過使用相對于當前請求(不是相對ServletContext的根路徑)路徑的相對路徑被獲得,ServletRequest 接口中提供了getRequestDispatcher 方法产弹。
這個方法的行為與ServletContext 中有相同名字的方法類似派歌。servlet容器使用請求對象中的信息把給定的相對于當前servlet的路徑轉(zhuǎn)換為一個完整路徑。比如痰哨,在一個"/"根上下文胶果,一個請求/garden/tools.html中,通過ServletRequest.getRequestDispatcher("header.html") 獲得的請求分發(fā)器將像調(diào)用ServletContext.getRequestDispatcher("/garden/header.html") 一樣斤斧。
-
請求分發(fā)器路徑中的查詢字符串
使用路徑信息來創(chuàng)建RequestDispatcher對象的ServletContext和ServletRequest方法允許請求字符串信息的可選附件給這個路徑早抠。比如,一個開發(fā)者可以通過下列代碼獲得一個RequestDispatcher:String path = "/raisins.jsp?orderno=5"; RequestDispatcher rd = context.getRequestDispatcher(path); rd.include(request, response);
被用來創(chuàng)建RequestDispatcher的請求字符串中指定的參數(shù)相比傳遞給servlet的有相同名字的參數(shù)有更高優(yōu)先級撬讽。與一個RequestDispatcher 關(guān)聯(lián)的參數(shù)僅被應(yīng)用在include和forward 調(diào)用期間蕊连。
二、使用一個請求分發(fā)器
為了使用一個請求分發(fā)器游昼,一個servlet調(diào)用RequestDispatcher 接口的include或者forward方法甘苍。給這些方法的參數(shù)可能是通過javax.servlet接口的service方法傳遞進來的request和response參數(shù),或者是request或response包裝對象類的子類實例烘豌。對于后者载庭,包裝器實例必須包裝容器傳遞給service方法的request或者response對象。
容器提供者應(yīng)該確保請求一個目標servlet的分發(fā)出現(xiàn)在相同JVM里的相同線程廊佩。
三囚聚、Include方法
RequestDispatcher接口的include方法可以在任意時間被調(diào)用。include方法的目標servlet可以獲得請求對象的所有方面标锄,它對response對象的使用會更加受限顽铸。
它僅能夠把信息寫到response對象的ServletOutputStream或者Writer,并且通過寫內(nèi)容超出response緩存鸯绿,或者顯示調(diào)用ServletResponse接口的flushBuffer方法來提交response跋破。
除了HttpServletRequest.getSession()和HttpServletRequest.getSession(boolean)方法,它不能設(shè)置頭部或者調(diào)用任何影響response頭部的方法瓶蝴。
任何嘗試設(shè)置頭部必須被忽略毒返,并且如果response已經(jīng)被提交,任何對需要添加一個Cookie響應(yīng)頭的HttpServletRequest.getSession()或者HttpServletRequest.getSession(boolean)的調(diào)用必須拋出一個IllegalStateException舷手。
如果默認servlet是RequestDispatch.include()的目標拧簸,并且請求的資源并不存在,那么這個默認的servlet必須拋出FileNotFoundException男窟。如果這個異常沒有被抓住和處理盆赤,并且response還沒有被提交贾富,這個狀態(tài)碼必須被設(shè)置為500。
-
Included Request 參數(shù)
除了用getNameDispatcher方法獲得的servlets牺六,一個被另外servlet使用RequestDispatcher接口的include方法調(diào)用的servlet可以訪問它被調(diào)用的路徑颤枪。
下列請求屬性必須被設(shè)置:javax.servlet.include.request_uri javax.servlet.include.context_path javax.servlet.include.servlet_path javax.servlet.include.path_info javax.servlet.include.query_string
這些屬性可以通過請求對象上從included servlet通過getAttribute方法訪問到,并且它們的值必須分別等于請求URI淑际,context path畏纲,servlet path,path info和included servlet的查詢字符串春缕。如果這個請求是后續(xù)included盗胀,這些屬性會被這個include代替。
如果這個included servlet通過getNameDispatcher方法被獲得锄贼,這些屬性一定不能被設(shè)置票灰。
四、Forward 方法
僅當輸出還未被提交到客戶端宅荤,RequestDispatcher接口的forward方法可以通過調(diào)用servlet而被調(diào)用屑迂。如果response緩存中的輸出數(shù)據(jù)還未被提交,在目標servlet的service方法被調(diào)用之前膘侮,其中的內(nèi)容必須被清除屈糊。如果response對象已經(jīng)被提交,IllegalStateException必須被拋出琼了。
暴露給目標servlet的請求對象的路徑元素必須反應(yīng)用來獲取RequestDispatcher的路徑。
對此夫晌,唯一的例外就是如果RequestDispatcher通過getNamedDispatcher方法獲得雕薪。這種場景下,請求對象的路徑元素必須反應(yīng)出原始請求的路徑晓淀。
在RequestDispatcher接口的forward方法沒有異常并返回之前所袁,response內(nèi)容必須被容器發(fā)送,提交和關(guān)閉凶掰。如果一個錯誤出現(xiàn)在RequestDispatcher.forward()的目標中燥爷,異常會在所有調(diào)用filters和servlets之中傳遞,最終傳遞回容器懦窘。
查詢字符串
當forwarding或者including請求時前翎,請求分發(fā)機制負責聚合查詢字符串參數(shù)。-
Forwarded請求參數(shù)
除了通過getNamedDispatcher方法獲得的servlets畅涂,一個被另外servlet通過使用RequestDispatcher的forward方法調(diào)用的servlet能夠訪問原始請求的路徑港华。
下列請求屬性必須被設(shè)置:javax.servlet.forward.request_uri javax.servlet.forward.context_path javax.servlet.forward.servlet_path javax.servlet.forward.path_info javax.servlet.forward.query_string
這些屬性的值必須分別等于HttpServletRequest的這些方法的返回值-getRequestURI,getContextPath午衰,getServletPath立宜,getPathInfo冒萄,getQueryString,這些方法在傳遞給來自客戶端的請求的調(diào)用鏈上第一個servlet對象的請求對象上被調(diào)用橙数。
這些屬性通過請求對象上的getAttribute方法從forwarded servlet訪問到尊流。需要注意的是,這些屬性必須總能反應(yīng)原始請求里面的信息灯帮,即使在多個forwards和后續(xù)請求被調(diào)用的情況下崖技。
如果forwarded servlet通過使用getNamedDispatcher方法獲得,那么這些屬性一定不能被設(shè)置施流。
五响疚、錯誤處理
如果是一個請求分發(fā)器目標的servlet拋出一個運行時異常或者一個ServletException或者IOException類型的已檢查異常瞪醋,它會被傳遞到發(fā)起調(diào)用的servlet忿晕。所有其它異常應(yīng)該被包裝為ServletException,并且把異常的根本原因設(shè)置為原始異常银受,就像它沒有被傳遞一樣践盼。
六宾巍、獲得一個AsyncContext
實現(xiàn)AsyncContext接口的一個對象可以通過一個startAsync方法從ServletRequest獲取。一旦你有一個AsyncContext顶霞,你能用它通過complete()方法或者一個dispatch方法的使用來完成請求的處理。
七选浑、Dispatch Method
下列來自AsyncContext的方法能夠用來分發(fā)請求:
- dispatch(path)
dispatch方法需要一個字符串參數(shù)蓝厌,用來描述ServletContext范圍里面的路徑。這個路徑必須相對于ServletContext的根路徑拓提,并且以'/'開頭。 - dispatch(servletContext, path)
dispatch方法需要一個字符串參數(shù)代态,用來描述指定ServletContext范圍里面的路徑。這個路徑必須相對于指定ServletContext的根路徑疹吃,并且以'/'開頭蹦疑。 - dispatch()
dispatch方法不需要參數(shù)。它使用原始URI作為路徑互墓。如果AsyncContext通過startAsync(ServletRequest,ServletResponse)初始化,并且傳遞進來的請求是HttpServletRequest的實例判莉,那么這個分發(fā)就是HttpServletRequest.getRequestURI()返回的URI豆挽。否則券盅,當它是容器的最后一個分發(fā),這次分發(fā)就是請求的URI娘侍。
一個AsyncContext接口的dispatch方法可以被等待異步事件發(fā)生的應(yīng)用調(diào)用。如果complete()已經(jīng)在AsyncContext上被調(diào)用憾筏,必須拋出一個IllegalStateException異常痒芝。所有dispatch方法立即返回并且并不提交response荆虱。
暴露給目標servlet的請求對象的路徑元素必須要反應(yīng)出AsyncContext.dispatch里面指定的路徑。
查詢字符串
當分發(fā)請求時拖叙,請求分發(fā)機制負責聚合查詢字符串钱骂。-
Dispatched 請求參數(shù)
一個通過使用AsyncContext接口的dispatch方法調(diào)用的servlet可以訪問到原始請求的路徑黄痪。
下列請求屬性必須被設(shè)置:javax.servlet.async.request_uri javax.setvlet.async.context_path javax.servlet.async.servlet_path javax.servlet.async.path_info javax.servlet.async.query_string
這些屬性的值必須分別等于HttpServletRequest接口的這些方法返回的值-getRequestURI, getContextPath, getServletPath, getPathInfo, getQueryString盔然,這些方法在傳遞給來自客戶端的請求的調(diào)用鏈上第一個servlet對象的請求對象上被調(diào)用。
這些屬性通過請求對象上的getAttribute方法從forwarded servlet訪問到油额。需要注意的是刻帚,這些屬性必須總能反應(yīng)原始請求里面的信息,即使在多個dispatches被調(diào)用的情況下崇众。
翻譯自 Java Servlet Specification
Version 3.0 Rev a
Author:Rajiv Mordani
Date: December 2010