<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆-57  評論-202  文章-17  trackbacks-0
    使用定制標記進行流控制和 URL 管理

    Mark A. Kolb
    軟件工程師
    2003 年 6 月 21 日

    顧名思義,JSP 標準標記庫(JSP Standard Tag Library,JSTL) core 庫為一些基本功能(如,管理限定了作用域的變量和與 URL 交互等)和基本操作(如,迭代和條件化)提供了定制標記。這些標記不僅可以由頁面設計人員直接利用,而且還為與其它 JSTL 庫相結合從而提供更復雜的表示邏輯奠定了基礎。Mark Kolb 在本文中繼續對 JSTL 和 core 庫進行探討,研究用標記來協助流控制和 URL 管理。

    通過閱讀本系列的 第一篇文章,您對 JSTL 有了初步的了解。我們描述了使用其 表達式語言(EL)來訪問數據和操作數據。正如您所了解的那樣,EL 用來為 JSTL 定制標記的屬性賦予動態值,因此,它所起的作用與 JSP 表達式一樣,為內置操作及其它定制標記庫指定請求時的屬性值。

    為了演示 EL 的用法,我們介紹了 core 庫中的三個標記: <c:set><c:remove><c:out><c:set><c:remove> 用于管理限定了作用域的變量;而 <c:out> 用于顯示數據,尤其是顯示用 EL 計算出的值。在此基礎上,接下來本文把注意力集中在 core 庫的其余標記上,這些標記可以大致歸為兩大類別:流控制和 URL 管理。

    示例應用程序
    為了演示 JSTL 標記,我們將使用來自一個工作應用程序的示例,本系列中余下的文章都將使用此應用程序。由于基于 Java 的 Weblog 日漸流行及為人們所熟悉,因此我們將出于此目的使用一個簡單的基于 Java 的 Weblog;參閱 參考資料以下載該應用程序的 JSP 頁面和源代碼。Weblog(也稱為 blog)是一種基于 Web 的簡短注釋的日志,這些注釋是有關 Weblog 的作者所感興趣的主題,通常帶有與 Web 上其它地方的相關文章及討論的鏈接。圖 1 中顯示了該應用程序正在運行時的抓屏。

    圖 1. Weblog 應用程序
    Weblog 示例應用程序的抓屏

    雖然完整的實現需要二十四個 Java 類,但在表示層中卻只涉及 Weblog 應用程序中的兩個類, EntryUserBean 。這樣,對于理解 JSTL 示例而言,只有這兩個類比較重要。圖 2 顯示了 EntryUserBean 的類圖。

    圖 2. Weblog 應用程序的類圖
    Weblog 示例應用程序的類圖

    Entry 類表示 Weblog 中一個標有日期的項。其 id 屬性用于在數據庫中存儲及檢索該項,而 titletext 屬性則表示該項的實際內容。 createdlastModified 屬性引用了 Java 語言中 Date 類的兩個實例,分別用來表示最初創建該項的時間和最后編輯該項的時間。 author 屬性引用了標識該項的創建者的 UserBean 實例。

    UserBean 類存儲了有關應用程序的已認證用戶的信息,如用戶名、全名和電子郵件地址。該類還包含一個用于與相關數據庫進行交互的 id 屬性。其最后一個屬性 roles 引用一列 String 值,這列值標識與相應用戶相關的、特定于應用程序的角色。對于 Weblog 應用程序,相關的角色是“User”(所有應用程序用戶常用的缺省角色)和“Author”(該角色指定可以創建和編輯 Weblog 項的用戶)。

    流控制
    由于可以用 EL 替代 JSP 表達式來指定動態屬性值,因此頁面創作人員無需使用腳本編制元素。因為腳本編制元素可能是引起 JSP 頁面中維護問題的主要原因,所以 JSTL 的主要優點就在于提供了這樣簡單(且標準)的替代方法。

    EL 從 JSP 容器檢索數據,遍歷對象層次結構,然后對結果執行簡單的操作。不過,除了訪問和操作數據之外,JSP 腳本編制元素的另一個常見用法是流控制。尤其是,頁面創作人員常借助 scriptlet 來實現迭代或條件內容。然而,因為這樣的操作超出了 EL 的能力,所以 core 庫提供了幾個定制操作來管理流控制,其形式有 迭代條件化異常處理

    迭代
    在 Web 應用程序環境中,迭代主要用于訪存和顯示數據集,通常是以列表或表中的一系列行的形式顯示。實現迭代內容的主要 JSTL 操作是 <c:forEach> 定制標記。該標記支持兩種不同樣式的迭代:整數范圍上的迭代(類似 Java 語言的 for 語句)和集合上的迭代(類似 Java 語言的 IteratorEnumeration 類)。

    進行整數范圍迭代用到了清單 1 中所示的 <c:forEach> 標記的語法。 beginend 屬性要么是靜態整數值,要么是可以得出整數值的表達式。它們分別指定迭代索引的初始值以及迭代索引的終止值。當使用 <c:forEach> 在整數范圍內進行迭代時,這兩個屬性是必需的,而其它所有屬性都是可選的。

    清單 1. 通過 <c:forEach> 操作進行數字迭代的語法
    <c:forEach var="
            name" varStatus="
            name"
        begin="
            expression" end="
            expression" step="
            expression">
      
            body content
    </c:forEach>
    
          

    當出現 step 時,它也必須是整數值。它指定每次迭代后索引的增量。這樣,迭代索引從 begin 屬性的值開始,以 step 屬性的值為增量進行遞增,在迭代索引超過 end 屬性的值時停止迭代。注:如果省略了 step 屬性,那么步長缺省為 1。

    如果指定了 var 屬性,那么將會創建一個帶有指定名稱的并限定了作用域的變量,并將每次迭代的當前索引值賦給該變量。這一限定了作用域的變量具有嵌套式可視性 ― 只可以在 <c:forEach> 標記體內對其進行訪問。(我們很快將討論可選屬性 varStatus 的用法。)清單 2 顯示了對一組固定整數值進行迭代的 <c:forEach> 操作示例。

    清單 2. 使用 <c:forEach> 標記來生成表列數據,這些數據對應于某一范圍內的數值
    <table>
    <tr><th>Value</th>
        <th>Square</th></tr>
    <c:forEach var="x" begin="0" end="10" step="2">
      <tr><td><c:out value="${x}"/></td>
          <td><c:out value="${x * x}"/></td></tr>
    </c:forEach>
    </table>
    

    如圖 3 中所示,上面的示例代碼生成了一張表,顯示前五個偶數及其平方。這是通過將 beginstep 屬性值指定為 2,而將 end 屬性值指定為 10 實現的。此外,用 var 屬性創建用于存儲索引值的限定了作用域的變量, <c:forEach> 標記體內引用了該變量。尤其是,使用了一對 <c:out> 操作來顯示索引及其平方,其中索引的平方是使用一個簡單的表達式計算得來的。

    圖 3. 清單 2 的輸出
    清單 2 的輸出

    在對集合的成員進行迭代時,用到了 <c:forEach> 標記的另一個屬性: items 屬性,清單 3 中顯示了該屬性。當使用這種形式的 <c:forEach> 標記時, items 屬性是唯一必需的屬性。 items 屬性的值應該是一個集合,對該集合的成員進行迭代,通常使用 EL 表達式指定值。如果變量名稱是通過 <c:forEach> 標記的 item 屬性指定的,那么對于每次迭代該已命名變量都將被綁定到集合后續元素上。

    清單 3. 通過 <c:forEach> 操作對集合進行迭代的語法
    <c:forEach var="
            name" items="
            expression" varStatus="
            name"
        begin="
            expression" end="
            expression" step="
            expression">
      
            body content
    </c:forEach>
    
          

    <c:forEach> 標記支持 Java 平臺所提供的所有標準集合類型。此外,您可以使用該操作來迭代數組(包括基本類型數組)中的元素。表 1 列出了 items 屬性所支持的所有值。正如表的最后一行所指出的那樣,JSTL 定義了它自己的接口 javax.servlet.jsp.jstl.sql.Result ,用來迭代 SQL 查詢的結果。(我們將在本系列后面的文章中詳細討論這一功能。)

    表 1. <c:forEach> 標記的 items 屬性所支持的集合
    items 的值 所產生的 item
    java.util.Collection 調用 iterator() 所獲得的元素
    java.util.Map java.util.Map.Entry 的實例
    java.util.Iterator 迭代器元素
    java.util.Enumeration 枚舉元素
    Object 實例數組 數組元素
    基本類型值數組 經過包裝的數組元素
    用逗號定界的 String 子字符串
    javax.servlet.jsp.jstl.sql.Result SQL 查詢所獲得的行

    可以使用 beginendstep 屬性來限定在迭代中包含集合中哪些元素。和通過 <c:forEach> 進行數字迭代的情形一樣,在迭代集合中的元素時同樣要維護一個迭代索引。 <c:forEach> 標記實際上只處理那些與索引值相對應的元素,這些索引值與指定的 beginendstep 值相匹配。

    清單 4 顯示了用來迭代集合的 <c:forEach> 標記。對于該 JSP 代碼段, entryList 這一限定了作用域的變量被設置成了 Entry 對象列表(確切的說, ArrayList )。 <c:forEach> 標記依次處理列表中的每個元素,將其賦給一個限定了作用域的變量 blogEntry ,然后生成兩個表行 ― 一個用于 Weblog 項的 title ,另一個則用于該項 text 。這些特性是通過一對帶有相應 EL 表達式的 <c:out> 操作從 blogEntry 變量檢索得到的。注:由于 Weblog 項的標題和文本都可能包含 HTML 標記,因此這兩個 <c:out> 標記的 escapeXml 屬性都被設置成了 false。圖 4 顯示了結果。

    清單 4. 使用 <c:forEach> 標記顯示表示給定日期的 Weblog 項
    <table>
      <c:forEach items="${entryList}" var="blogEntry">
        <tr><td align="left" class="blogTitle">
          <c:out value="${blogEntry.title}" escapeXml="false"/>
        </td></tr>
        <tr><td align="left" class="blogText">
          <c:out value="${blogEntry.text}" escapeXml="false"/>
        </td></tr>
      </c:forEach>
    </table>
    

    圖 4. 清單 4 的輸出
    清單 4 的輸出

    不論是對整數還是對集合進行迭代, <c:forEach> 剩余的屬性 varStatus 所起的作用相同。和 var 屬性一樣, varStatus 用于創建限定了作用域的變量。不過,由 varStatus 屬性命名的變量并不存儲當前索引值或當前元素,而是賦予 javax.servlet.jsp.jstl.core.LoopTagStatus 類的實例。該類定義了一組特性,它們描述了迭代的當前狀態,表 2 中列出了這些特性。

    表 2. LoopTagStatus 對象的特性
    特性 Getter 描述
    current getCurrent() 當前這次迭代的(集合中的)項
    index getIndex() 當前這次迭代從 0 開始的迭代索引
    count getCount() 當前這次迭代從 1 開始的迭代計數
    first isFirst() 用來表明當前這輪迭代是否為第一次迭代的標志
    last isLast() 用來表明當前這輪迭代是否為最后一次迭代的標志
    begin getBegin() begin 屬性值
    end getEnd() end 屬性值
    step getStep() step 屬性值

    清單 5 顯示了關于如何使用 varStatus 屬性的一個示例。這個示例修改了清單 4 中的代碼,將 Weblog 項的編號添加到顯示 Weblog 標題(title)的表行。它是通過為 varStatus 屬性指定一個值,然后訪問所生成的限定了作用域的變量的 count 特性來實現這一點的。結果顯示在圖 5 中。

    清單 5. 使用 varStatus 屬性來顯示 Weblog 項的數目
    <table>
      <c:forEach items=
        "${entryList}" var="blogEntry" varStatus="status">
        <tr><td align="left" class="blogTitle">
          <c:out value="${status.count}"/>.
          <c:out value="${blogEntry.title}" escapeXml="false"/>
        </td></tr>
        <tr><td align="left" class="blogText">
          <c:out value="${blogEntry.text}" escapeXml="false"/>
        </td></tr>
      </c:forEach>
    </table>
    

    圖 5. 清單 5 的輸出
    清單 5 的輸出

    <c:forEach> 以外, core 庫還提供了另一個迭代標記: <c:forTokens> 。JSTL 的這個定制操作與 Java 語言的 StringTokenizer 類的作用相似。清單 6 中顯示的 <c:forTokens> 標記除了比 <c:forEach> 的面向集合版本多一個屬性之外,其它屬性都相同。對于 <c:forTokens> 而言,通過 items 屬性指定要標記化的字符串,而通過 delims 屬性提供用于生成標記的一組定界符。和 <c:forEach> 的情形一樣,可以使用 beginendstep 屬性將要處理的標記限定為那些與相應索引值相匹配的標記。

    清單 6. 使用 <c:forTokens> 操作來迭代字符串標記的語法
    <c:forTokens var="
            name" items="
            expression"
        delims="
            expression" varStatus="
            name"
        begin="
            expression" end="
            expression" step="
            expression">
      
            body content
    </c:forTokens>
    
          

    條件化
    對于包含動態內容的 Web 頁面,您可能希望不同類別的用戶看到不同形式的內容。例如,在我們的 Weblog 中,訪問者應該能夠閱讀各項,也許還應該能夠提交反饋,但只有經過授權的用戶才能公布新項,或編輯已有內容。

    在同一個 JSP 頁面內實現這樣的功能,然后使用條件邏輯來根據每條請求控制所顯示的內容,這樣做常常能夠改善實用性和軟件維護。 core 庫提供了兩個不同的條件化標記 ― <c:if><c:choose> ― 來實現這些功能。

    <c:if> 是這兩個操作中較簡單的一個,它簡單地對單個測試表達式進行求值,接下來,僅當對表達式求出的值為 true 時,它才處理標記的主體內容。如果求出的值不為 true ,就忽略該標記的主體內容。如清單 7 所示, <c:if> 可以通過其 varscope 屬性(它們所起的作用和在 <c:set> 中所起的作用一樣)選擇將測試結果賦給限定了作用域的變量。當測試代價非常高昂時,這種能力尤為有用:可以將結果高速緩存在限定了作用域的變量中,然后在隨后對 <c:if> 或其它 JSTL 標記的調用中檢索該結果。

    清單 7. <c:if> 條件操作的語法
    <c:if test="
            expression" var="
            name" scope="
            scope">
      
            body content
    </c:if>
    
          

    清單 8 顯示了與 <c:forEach> 標記的 LoopTagStatus 對象的 first 特性一起使用的 <c:if> 。如圖 6 中所示,在這種情況下,只在 Weblog 項的第一項上顯示這組項的創建日期,而不在任何其它項前面重復該日期。

    清單 8. 使用 <c:if> 來為 Weblog 項顯示日期
    <table>
      <c:forEach items=
        "${entryList}" var="blogEntry" varStatus="status">
        <c:if test="${status.first}">
          <tr><td align="left" class="blogDate">
                <c:out value="${blogEntry.created}"/>
          </td></tr>
        </c:if>
        <tr><td align="left" class="blogTitle">
          <c:out value="${blogEntry.title}" escapeXml="false"/>
        </td></tr>
        <tr><td align="left" class="blogText">
          <c:out value="${blogEntry.text}" escapeXml="false"/>
        </td></tr>
      </c:forEach>
    </table>
    

    圖 6. 清單 8 的輸出
    清單 8 的輸出

    如清單 8 所示, <c:if> 標記為條件化內容的一些簡單情形提供了一種非常簡潔的表示法。對于需要進行互斥測試來確定應該顯示什么內容的情況下,JSTL core 庫還提供了 <c:choose> 操作。清單 9 中顯示了 <c:choose> 的語法。

    清單 9. <c:choose> 操作的語法
    <c:choose>
      <c:when test="
            expression">
        
            body content
      </c:when>
      ...
      <c:otherwise>
        
            body content
      </c:otherwise>
    </c:choose>
    
          

    每個要測試的條件都由相應的 <c:when> 標記來表示,至少要有一個 <c:when> 標記。只會處理第一個其 test 值為 true<c:when> 標記體內的內容。如果沒有一個 <c:when> 測試返回 true ,那么會處理 <c:otherwise> 標記的主體內容。注:盡管如此, <c:otherwise> 標記卻是可選的; <c:choose> 標記至多可有一個嵌套的 <c:otherwise> 標記。如果所有 <c:when> 測試都為 false ,而且又沒有給出 <c:otherwise> 操作,那么不會處理任何 <c:choose> 標記的主體內容。

    清單 10 顯示了運用 <c:choose> 標記的示例。在這里,檢索請求對象而獲得協議信息(通過 EL 的 pageContext 隱式對象),并用簡單的字符串比較對協議信息進行測試。根據這些測試的結果,會顯示相應的文本消息。

    清單 10. 使用 <c:choose> 進行內容條件化
    <c:choose>
      <c:when test="${pageContext.request.scheme eq 'http'}">
        This is an insecure Web session.
      </c:when>
      <c:when test="${pageContext.request.scheme eq 'https'}">
        This is a secure Web session.
      </c:when>
      <c:otherwise>
        You are using an unrecognized Web protocol. How did this happen?!
      </c:otherwise>
    </c:choose>
    

    異常處理
    最后一個流控制標記是 <c:catch> ,它允許在 JSP 頁面內進行初級的異常處理。更確切地說,在該標記的主體內容中產生的任何異常都會被捕獲并被忽略(即,不會調用標準的 JSP 錯誤處理機制)。然而,如果產生了一個異常并且已經指定了 <c:catch> 標記的可選屬性 var ,那么會將異常賦給(具有頁面作用域的)指定的變量,這使得能夠在頁面自身內部進行定制錯誤處理。清單 11 顯示了 <c:catch> 的語法(稍后在 清單 18中給出一個示例)。

    清單 11. <c:catch> 操作的語法
    <c:catch var="
            name">
      
            body content
    </c:catch>
    
          

    URL 操作
    JSTL core 庫中的其余標記主要是關于 URL。這些標記中的第一個被適當地命名為 <c:url> 標記,用于生成 URL。尤其是, <c:url> 提供了三個功能元素,它們在為 J2EE Web 應用程序構造 URL 時特別有用:

    • 在前面附加當前 servlet 上下文的名稱
    • 為會話管理重寫 URL
    • 請求參數名稱和值的 URL 編碼

    清單 12 顯示了 <c:url> 標記的語法。 value 屬性用來指定基本 URL,然后在必要時標記對其進行轉換。如果這個基本 URL 以一個斜杠開始,那么會在它前面加上 servlet 的上下文名稱。可以使用 context 屬性提供顯式的上下文名稱。如果省略該屬性,那么就使用當前 servlet 上下文的名稱。這一點特別有用,因為 servlet 上下文名稱是在部署期間而不是開發期間決定的。(如果這個基本 URL 不是以斜杠開始的,那么就認為它是一個相對 URL,這時就不必添加上下文名稱。)

    清單 12. <c:url> 操作的語法
    <c:url value="
            expression" context="
            expression"
        var="
            name" scope="
            scope">
      <c:param name="
            expression" value="
            expression"/>
      ...
    </c:url>
    
          

    URL 重寫是由 <c:url> 操作自動執行的。如果 JSP 容器檢測到一個存儲用戶當前會話標識的 cookie,那么就不必進行重寫。但是,如果不存在這樣的 cookie,那么 <c:url> 生成的所有 URL 都會被重寫以編碼會話標識。注:如果在隨后的請求中存在適當的 cookie,那么 <c:url> 將停止重寫 URL 以包含該標識。

    如果為 var 屬性提供了一個值(還可以同時為 scope 屬性提供一個相應的值,這是可選的),那么將生成的 URL 賦值給這個限定了作用域的指定變量。否則,將使用當前的 JspWriter 輸出生成的 URL。這種直接輸出其結果的能力允許 <c:url> 標記作為值出現,例如,作為 HTML <a> 標記的 href 屬性的值,如清單 13 中所示。

    清單 13. 生成 URL 作為 HTML 標記的屬性值
    <a href="<c:url value='/content/sitemap.jsp'/>">View sitemap</a>
    

    最后,如果通過嵌套 <c:param> 標記指定了任何請求參數,那么將會使用 HTTP GET 請求的標準表示法將它們的名稱和值添加到生成的 URL 后面。此外,還進行 URL 編碼:為了生成有效的 URL,將對這些參數的名稱或值中出現的任何字符適當地進行轉換。清單 14 演示了 <c:url> 的這種行為。

    清單 14. 生成帶請求參數的 URL
    <c:url value="/content/search.jsp">
      <c:param name="keyword" value="${searchTerm}"/>
      <c:param name="month" value="02/2003"/>
    </c:url>
    

    清單 14 中的 JSP 代碼被部署到一個名為 blog 的 servlet 上下文,限定了作用域的變量 searchTerm 的值被設置為 "core library" 。如果檢測到了會話 cookie,那么清單 14 生成的 URL 將類似于清單 15 中的 URL。注:在前面添加上下文名稱,而在后面附加請求參數。此外, keyword 參數值中的空格和 month 參數值中的斜杠都被按照 HTTP GET 參數的需要進行了編碼(確切地說,空格被轉換成了 + ,而斜杠被轉換成了 %2F 序列)。

    清單 15. 有會話 cookie 時生成的 URL
    /blog/content/search.jsp?keyword=foo+bar&month=02%2F2003
    

    當沒有會話 cookie 時,生成的結果如清單 16 中所示。同樣,servlet 上下文被添加到了前面,而 URL 編碼的請求參數被附加到了后面。不過,除此以外還重寫了基本 URL 以包含指定的會話標識。當瀏覽器發送用這種方式重寫的 URL 請求時,JSP 容器自動抽取會話標識,并將請求與相應的會話進行關聯。這樣,需要會話管理的 J2EE 應用程序就無需依賴由應用程序用戶啟用的 cookie 了。

    清單 16. 沒有會話 cookie 時生成的 URL
    /blog/content/search.jsp;jsessionid=233379C7CD2D0ED2E9F3963906DB4290
      ?keyword=foo+bar&month=02%2F2003
    

    導入內容
    JSP 有兩種內置機制可以將來自不同 URL 的內容合并到一個 JSP 頁面: include 偽指令和 <jsp:include> 操作。不過,不管是哪種機制,要包含的內容都必須屬于與頁面本身相同的 Web 應用程序(或 servlet 上下文)。兩個標記之間的主要區別在于: include 偽指令在頁面編譯期間合并被包含的內容,而 <jsp:include> 操作卻在請求處理 JSP 頁面時進行。

    從本質上講, core 庫的 <c:import> 操作是更通用、功能更強大的 <jsp:include> 版本(好像是 <jsp:include> “服用了興奮劑”的)。和 <jsp:include> 一樣, <c:import> 也是一種請求時操作,它的基本任務就是將其它一些 Web 資源的內容插入 JSP 頁面中。如清單 17 中所示,它的語法非常類似于 <c:url> 的語法。

    清單 17. <c:import> 操作的語法
    <c:import url="
            expression" context="
            expression"
        charEncoding="
            expression" var="
            name" scope="
            scope">
      <c:param name="
            expression" value="
            expression"/>
      ...
    </c:import>
    
          

    通過 url 屬性指定將要導入內容的 URL,這個屬性是 <c:import> 的唯一一個必選屬性。這里允許使用相對 URL,并且根據當前頁面的 URL 來解析這個相對 URL。但是,如果 url 屬性的值以斜杠開始,那么它就被解釋成本地 JSP 容器內的絕對 URL。如果沒有為 context 屬性指定值,那么就認為這樣的絕對 URL 引用當前 servlet 上下文內的資源。如果通過 context 屬性顯式地指定了上下文,那么就根據指定的 servlet 上下文解析絕對(本地)URL。

    <c:import> 操作并不僅僅限于訪問本地內容。也可以將包含協議和主機名的完整 URI 指定為 url 屬性的值。實際上,協議甚至不僅局限于 HTTP。 <c:import>url 屬性值可以使用 java.net.URL 類所支持的任何協議。清單 18 中顯示了這種能力。

    其中, <c:import> 操作用來包含通過 FTP 協議訪問的文檔內容。此外,還使用了 <c:catch> 操作,以便在本地處理 FTP 文件傳送期間可能發生的任何錯誤。錯誤處理是這樣實現的:使用 <c:catch>var 屬性為異常指定一個限定了作用域的變量,然后使用 <c:if> 檢查其值。如果產生了異常,那么就會對那個限定了作用域的變量進行賦值:如清單 18 中的 EL 表達式所顯示的那樣,該變量的值將 會為空。由于 FTP 文檔的檢索將會失敗,因此會顯示有關這種情況的錯誤消息。

    清單 18. 將 <c:import> 與 <c:catch> 相結合的示例
    <c:catch var="exception">
      <c:import url="ftp://ftp.example.com/package/README"/>
    </c:catch>
    <c:if test="${not empty exception}">
      Sorry, the remote content is not currently available.
    </c:if>
    

    <c:import> 操作的最后兩個(可選的)屬性是 varscopevar 屬性會導致從指定 URL 獲取的內容(作為 String 值)被存儲在一個限定了作用域的變量中,而不是包含在當前 JSP 頁面中。 scope 屬性控制該變量的作用域,缺省情況下是頁面作用域。如同我們在今后的文章中將要看到的那樣,JSTL xml 庫中的標記利用了 <c:import> 這種能力,即將整個文檔存儲在一個限定了作用域的變量中。

    還要注意的是,可以使用(可選的)嵌套的 <c:param> 標記來為正在導入的 URL 指定請求參數。與在 <c:url> 中嵌套 <c:param> 標記一樣,必要時也要對參數名稱和參數值進行 URL 編碼。

    請求重定向
    最后一個 core 庫標記是 <c:redirect> 。該操作用于向用戶的瀏覽器發送 HTTP 重定向響應,它是 JSTL 中與 javax.servlet.http.HttpServletResponsesendRedirect() 方法功能相當的標記。清單 19 中顯示了該標記的 urlcontext 屬性,它們的行為分別等同于 <c:import>urlcontext 屬性的行為,是嵌套任何 <c:param> 標記的結果。

    清單 19. <c:redirect> 操作的語法
    <c:redirect url="
            expression" context="
            expression">
      <c:param name="
            expression" value="
            expression"/>
      ...
    </c:redirect>
    
          

    清單 20 顯示了 <c:redirect> 操作,它用一個到指定錯誤頁面的重定向代替了清單 18 中的錯誤消息。在該示例中, <c:redirect> 標記的用法與標準 <jsp:forward> 操作的用法類似。不過請回憶一下:通過請求分派器進行轉發是在服務器端實現的,而重定向卻是由瀏覽器來執行的。從開發人員的角度來講,轉發比重定向更有效率,但 <c:redirect> 操作卻更靈活一些,因為 <jsp:forward> 只能分派到當前 servlet 上下文內的其它 JSP 頁面。

    清單 20. 響應異常的重定向
    <c:catch var="exception">
      <c:import url="ftp://ftp.example.com/package/README"/>
    </c:catch>
    <c:if test="${not empty exception}">
      <c:redirect url="/errors/remote.jsp"/>
    </c:if>
    

    從用戶的角度來看,主要區別在于重定向會更新瀏覽器所顯示的 URL,并因此影響書簽的設置。轉發卻不這樣,它對最終用戶是透明的。這樣,選擇 <c:redirect> 還是 <jsp:forward> 還取決于所期望的用戶體驗。

    結束語
    JSTL core 庫含有多種通用的定制標記,廣大的 JSP 開發人員都會使用這些標記。例如,URL 和異常處理標記很好地補充了現有的 JSP 功能,如 <jsp:include><jsp:forward> 操作、 include 偽指令以及 page 偽指令的 errorpage 屬性。迭代和條件操作使得無需腳本編制元素就能夠實現復雜的表示邏輯,尤其在將變量標記( <c:set><c:remove> )與 EL 相結合使用時更是如此。

    參考資料

    關于作者
    Mark Kolb 是一位在德克薩斯州奧斯汀市(Austin)工作的軟件工程師。他常就服務器端 Java 這一主題在業界演講,而且還是 Web Development with JavaServer Pages, 第二版 一書的合著者。可以通過 mak@taglib.com與 Mark 聯系。
    posted on 2005-06-06 15:25 小米 閱讀(398) 評論(0)  編輯  收藏 所屬分類: Java
    主站蜘蛛池模板: 国产精品成人免费福利| 四虎影视永久在线精品免费| 最近中文字幕免费大全| 免费A级毛片无码A| 国产精品日本亚洲777| 午夜毛片不卡高清免费| 亚洲精品乱码久久久久久蜜桃图片| 16女性下面扒开无遮挡免费| 91嫩草私人成人亚洲影院| 久久精品免费视频观看| 亚洲伊人久久大香线蕉苏妲己| 嫩草在线视频www免费观看| 久久久影院亚洲精品| 一区二区免费视频| 亚洲综合亚洲国产尤物| 国产免费看JIZZ视频| 亚洲熟妇无码AV| 免费一级毛片在级播放| 亚洲视频在线免费| 亚洲av日韩av不卡在线观看| 一级毛片不卡片免费观看| 亚洲一区二区三区高清视频| 福利免费观看午夜体检区| 亚洲s码欧洲m码吹潮| 亚洲成av人片一区二区三区| 9i9精品国产免费久久| 亚洲不卡av不卡一区二区| 88xx成人永久免费观看| 亚洲午夜福利在线视频| 免费一级毛片正在播放| 美女无遮挡拍拍拍免费视频| 亚洲国产另类久久久精品小说 | 啦啦啦手机完整免费高清观看| 亚洲Av永久无码精品一区二区| 亚洲av麻豆aⅴ无码电影| 午夜影院免费观看| 亚洲一区二区三区写真| 中文字幕亚洲专区| 无码乱肉视频免费大全合集| 校园亚洲春色另类小说合集 | 亚洲视频免费一区|