Servlet控制共享(RequestDispatcher)
public RequestDispatcher ServletRequest.getRequestDispatcher(String path) ,可以是相對路徑但不能超過當前的servlet context,不過可以使用getContext方法轉發到當前context之外的地方,只是沒有任何方法轉發到另一個服務器上的context。如果路徑以“/”開始,它被解釋為相對于當前context根,如果路徑包含查詢字符,參數會被包含到接受組件的參數集的開始處。如果servlet容器由于任何原因不能返回RequestDispatcher 該方法返回null。在ServletContext類中有同名方法
public RequestDispatcher ServletContext.getRequestDispatcher(String path),不同之處在于ServletContext 中的方法(sevlet2.1引入)只接受絕對URLs(以/開頭),而ServletRequest 中的方法接受絕對和相對。因此沒有理由使用ServletContext中的該方法,可以看作只為歷史版本兼容(不過servlet2.5仍然有此方法)。除了用路徑還可以用名字指定資源來獲得RequestDispatcher,ServletContext中的方法:
public RequestDispatcher ServletContext.getNamedDispatcher(String name),此方法允許轉發到沒必要公開發布的資源,servlet或jsp可能通過web應用描述符獲得名字。不能獲取RequestDispatcher仍然返回null。
RequestDispatcher有兩個方法:forward()and include()。forward把整個請求交給代理;include把代理的輸出加入調用servlet(個人理解為初始的那個servlet)的response中并且調用servlet仍然處于控制狀態,即請求轉發后,原先的Servlet還可以繼續輸出響應信息,轉發到的Servlet對請求做出的響應將并入原先Servlet的響應對象中
forward把請求從一個servlet轉發到服務器上的另一個資源,它允許一個servlet對請求做初始處理,然后另一個資源來產生response,不像sendRedirect,forward的整個操作都在服務器內部,對客戶是透明的。附加查詢字符串或使用請求的setAttribute的方法可以傳遞信息給代理 。
The rules a forwarding servlet must follow are relatively strict:
-
It may set headers and the status code but may not send any response body to the client (that's the job for an include). Consequently, the forward( ) must be called before the response has been committed.
-
If the response already has been committed, the forward( ) call throws an IllegalStateException.
-
If the response has not been committed but there's content within the response buffer, the buffer is automatically cleared as part of the forward.
-
In addition, you can't get creative by substituting new request and response objects. The forward( ) method must be called with the same request and response objects as were passed to the calling servlet's service method, and the forward( ) must be called from within the same handler thread.
如果servlet A轉發到servlet B,B得到的路徑信息應該是和直接調用B一樣的,從這方面來說B完全控制請求。這也是為什么確保response的緩存會在調用之前沖掉,response沒有被提交是很重要的事情。
用路徑轉發的問題是:目標組件不但對服務器組件可見,也必須對客戶端可見。出于安全考慮,可能會讓一些組件不是公共可見的,所以用名字代替路徑做轉發:getNamedDispatcher 不過沒有URI路徑也就不能加上查詢字符串,路徑也不能做調整。
forward 和sendRedirect誰更好:request dispatch 在服務器端發生,redirect在客戶端發生。forward最好用在一個組件必須處理業務邏輯而且和另一個組件分享結果的情況。sendRedirict最好用于客戶需要從一頁重定向于另一頁??雌饋碛胒orward比較有誘惑,因為forward在服務器內部完成,sendRedirect還需要從客戶端倒一遍,所以forward更快。不幸的是處理相對URL的時候會引起問題。例:
public class HomePageForward extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
RequestDispatcher dispatcher = req.getRequestDispatcher("/index.html");
dispatcher.forward(req, res);
}
}
如果index.html中有<img src="../../../images/trans.gif"/>則圖片不能讀取。sendRedirect會告訴客戶端文件來源于文件根目錄,forward不會。因為不需要調用getContext查詢,sendRedirect更容易轉發到其他context的資源中。所以建議是盡量用sendRedirect,必須的情況下才用forward。
RequestDispatcher的include方法把資源的內容放入當前的respongse中。它實現了一種我們可以稱為編程式服務器端 include。不同于forward,在調用include后的servlet A仍然控制response而且可能包含 被包含進來內容的前后內容。
例1:
doGet(..){
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println("<HTML><HEAD><TITLE>Welcome to Nile</TITLE></HEAD>");
out.println("<BODY>");
// Show an item in an online catalog
out.println("Feast your eyes on this beauty:");
RequestDispatcher dispatcher =
req.getRequestDispatcher("/servlet/NileItem?item=0596000405");
dispatcher.include(req, res);
out.println("And, since I like you, it's 20% off!");
out.println("</BODY></HTML>");
}
doGet(..){
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println("<HTML><HEAD><TITLE>Welcome to Nile</TITLE></HEAD>");
out.println("<BODY>");
// Show items in an online catalog
RequestDispatcher dispatcher =
req.getRequestDispatcher("/servlet/NileItem");
out.println("Feast your eyes on this beauty:");
req.setAttribute("item", Book.getBook("0596000405"));
dispatcher.include(req, res);
// Remove the "item" attribute after use
req.removeAttribute("item");
out.println("Or how about this one:");
req.setAttribute("item", Book.getBook("0395282659"));
dispatcher.include(req, res);
out.println("And, since I like you, they're all 20% off!");
out.println("</BODY></HTML>");
}
接受servlet
public class NileItem extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
// We do not set the content type
PrintWriter out = res.getWriter();
Book book = (Book) req.getAttribute("item");
out.println("<BR>");
if (book != null) {
out.println("<I>" + book.getTitle() + "</I>");
out.println(" by " + book.getAuthor());
}
else {
out.println("<I>No book record found</I>");
}
out.println("<BR>");
}
}
通過實例可以很好理解include,就是把下一個資源的內容直接放入本頁面的任何地方,被包含的資源不能改變status code or HTTP headers ,所以NileItem 做任何改變這些的操作都會被忽略(NileItem 所以沒有做這些操作)。不像forward,路徑元素和請求參數仍然和調用端一樣。如果被包含資源要求獲得自己的路徑元素和請求參數, it may retrieve them using the following server-assigned request attributes:
javax.servlet.include.request_uri
javax.servlet.include.context_path
javax.servlet.include.servlet_path
javax.servlet.include.path_info
The request and response parameters must be the same objects as were passed to the calling servlet's service method, and the include( ) must be called from within the same handler thread.
The included resource must use an output mechanism that matches the caller's. If the caller uses a PrintWriter, then the included resource must use a PrintWriter. If the caller uses an OutputStream, then the included resource must use an OutputStream. If the mechanisms don't match, the included servlet throws an IllegalStateException. If you can, make sure to use the PrintWriter for all text output, and this won't be a problem.
posted on 2009-11-05 17:11 yuxh 閱讀(1711) 評論(0) 編輯 收藏 所屬分類: j2ee