DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> XML學習教程 >> XML詳解 >> 使用 XML: 安全編碼實踐(2)
使用 XML: 安全編碼實踐(2)
編輯:XML詳解     

這一期文章中,我繼續討論常見的 XML 陷阱以及(可能更重要)如何避免這些陷阱。上一期專欄主要圍繞著 XML 語法本身使用中的一些常見誤解進行的。本文將考察如何在應用程序中結合 XML 支持來提高效率和可維護性。

  編程語言和開發工具(包括數據庫、IDE 和建模工具)對 XML 的支持不斷增強。撰寫本文的時候,Java 技術至少有 5 種與 XML 有關的正式 API:

  Java Architecture for XML Binding (JAXB)

  Java API for XML Processing (JAXP),可能是最龐大的 API,包括 4 部分:SAX、DOM、TrAX 和 XPath API

  Java API for XML RegistrIEs (JAXR)

  Java API for XML-based RPC (JAX-RPC)

  SOAP with Attachments API for Java (SAAJ)

  另外,還可以找到數不清的非正式 Java API,如 JDOM、PDOM、Castor 和 StAX(最終將集成到 JAXP 中)。而且重要的是,多數項目都對標准 API 提供了擴展,如 JAX-RPC 的 Axis 擴展。

  選擇太多了,令人無所適從。如果最後期限很短(這一行中通常如此),就毫不奇怪開發人員為什麼常常選擇最流行的 API(按照我的經驗一般是 DOM),不再審慎考慮所作出的選擇。

  與此類似,誰有時間去設計一個 XML 詞匯表呢?直接將對象層次轉成 XML 標簽似乎要快得多。

  但是這方面的決策對能否在規定的時間和預算內交付產品有很大的影響,更不必說維護應用程序的難易程度了。

  一句忠告:選擇一種設計是不同特性之間權衡的結果。沒有一種設計適合所有的需要,因此要花點時間對照應用程序需求來驗證這種權衡。但我發現,與其他設計相比,有些設計似乎是更好的起點,後面我將對這些設計加以說明。

  用 XML 作為接口

  從設計的觀點來看,這有助於把焦點放在 XML 文檔的角色上,而不是放在它們的結構上。從結構上說,XML 文檔是存儲數據的倉庫。更有趣的是,可以將 XML 文檔用作應用程序之間的接口。

  在這種情況下,一個應用程序將准備 XML 文檔,而另一個應用程序則消費文檔。這樣的例子可以找到很多:XML 編輯器保存後的文檔用於內容管理程序;新聞讀者從 Web 服務器上下載 Atom 或 RSS;SOAP 客戶機向服務器發送 SOAP 請求;Eclipse 平台讀取 XML 插件描述文件,等等。

  即使在最簡單的情況下,即應用程序生成 XML 文件供自己消費,仍然可以將文檔看作是應用程序兩次運行(可能恰好是不同的版本)之間的接口。

  如果將它看作是一個接口設計問題,那麼用於 JavaBeans 和其他 API 的規則同樣適用於 XML 設計。

  契約式設計

  已經提出的關於接口設計的很多方法論中,關系最密切的是 Eiffel 語言最先引入的契約式設計方法。

  契約式設計的核心是,按照與其他組件簽定的契約,詳細說明每個組件的功能需求。同法律契約一樣,契約中包含每個組件的責任和權利,即該組件提供什麼,以及需要其他組件提供了什麼。

  從實踐上來看,功能需求是根據數據結構和先決/後決條件來表達的。由於能夠使用模式語言(如 W3C XML Schema 和 RELAX NG)以及 Schematron 這樣的結構化驗證語言,所以 XML 非常適合於這種方法。

  定義的詞匯表變成了組件之間的契約。

  彈性

  應用程序的功能需求肯定會隨時間的推移而發生變化,支持它的 XML 詞匯表也將發生變化。但是為了確保最後期限的瘋狂沖刺,詞匯表的設計很少考慮能否應付測試階段。兩個常見的問題是缺少版本控制方案和依賴於對象數據模型。

  版本控制方案支持向後兼容,使以前的應用程序能夠處理較新的應用程序生成的文件,反之亦然。良好的版本控制方案使:

  新的應用程序能以兼容模式工作。

  以前的應用程序能夠打開新的文件而不會崩潰。

  第一點很容易通過包含 version 標簽或屬性來實現。如果版本低於當前版本,應用程序必須求助於向後兼容性。

  第二點需要更多的工作。因為不能修改以前的代碼(否則該代碼就是一個新應用程序了),所以必須從一開始就包括向前兼容性。具體而言,代碼必須:

  知道如何處理新的標簽,比如忽略或者報告錯誤(但是不能崩潰)。

  在文件破壞向後兼容性時,應該能夠檢測並報告錯誤。

  後一點常常被忽視。新版本詞匯表中所做的修改可能要求老的閱讀程序不應處理該文件。版本控制方案必須提供一種機制報告這種情況,比如用名為 compatibleWith 的標簽或屬性規定處理該文檔所需要的最小版本號。應用程序應該拒絕打開 compatibleWith 編號比自己的版本號高的文檔。清單 1 給出了一個例子。

  清單 1. 簡單的版本控制方案

  

  
<v:root compatibleWith="3" version="5"
  XMLns:v="http://psol.com/2005/sample">
  <!-- content goes here -->
</v:root>

  清單 1 是這個特定應用程序的版本 5 編寫的,版本 3、版本 4 或版本 5 都可以處理它,但是不能用版本 1 或版本 2 處理它。

  新的應用程序也可以使用不同的名稱空間來表示不兼容的修改。

  數據模型和詞匯表

  通常,人們已經圍繞著應用程序的對象模型作了大量開發工作,因此,利用這些成果並從對象模型中衍生出 XML 模型似乎是合理的。

  但實際上,這樣可能造成維護方面的惡夢。構成好的對象模型的那些特點變成了 XML 詞匯表的負擔:

  對象模型常常包含冗余信息,比如用散列表加快搜索的速度,而冗余信息卻意味著 XML 文檔需要更多的驗證。

  相反,模型可以動態地計算出為了進行驗證而記錄的信息。比如,對於在線購買,需要存儲的不僅僅是每個產品行,還包括總價格,因為買主已經認可了總價格。在對象模型中,根據每個產品項重新計算總價格是完全合理的。

  雖然正在使用的開發工具和庫不被認為是純粹的 對象設計,但它們常常影響對象模型。比如,如果使用了一個小部件(widget)庫,那麼可能需要調整模型,以適應這些部件。

  對象模型的一部分可以使用位掩碼(bit mask)或原生數組來優化,並犧牲可讀性來提高速度。

  最後,對象模型不需要在應用程序的不同版本間保持穩定,在軟件的演化中常常增加和刪除屬性。

  如果 XML 詞匯表是對象模型的直接映射,那麼它不可能很穩定。這在開發中可能引起一些問題,因為需要不斷地修改與 XML 有關的代碼,所以在維護中甚至會造成更多的問題。要記住的是,文檔是一個借口,因此必須在可行的情況下將其從實現細節中隔離出來。

  相反,如果花點時間確定對象模型中穩定的關系,那麼可以用最小的代價派生出更穩定的詞匯表。使用 UML 和原型(stereotype),甚至可以從一個圖同時生成對象模型和 XML 詞匯表(請參閱參考資料中“UML, XMI, and code generation”一文)。

  作為替換,可以把焦點集中到數據模型的功能 視圖上。對象模型是實現應用程序各個細節的一種技術視圖,隨著技術的演化而改變。但是功能視圖要穩定得多,應用程序即便遷移到新的平台上,基本上仍然可以提供相同的一組服務。

  從應用程序一方來看

  現在已經討論了 XML 詞匯表,還需要考察一下應用程序。同樣,基本的原理是將 XML 處理看作是一種接口來封裝。

  最糟糕的情況(我恐懼地發現經常如此)是圍繞著 DOM(或者 JDOM)樹來設計應用程序。除了少數例外(比如浏覽器),DOM 使用起來一直都是一種可怕的對象模型。

  DOM 看起來很有吸引力,因為它容易使用而且相當通用。很多開發人員認為,它能勝任交給它的任何任務,而且多數情況下確實如此。很多開發人員通過 DOM 學習了 XML 編程,因此認定 DOM 是最合理的選擇。

  DOM 是 W3C 為一類限制相當嚴格的應用程序設計的,這類應用程序就是 Web 浏覽器。對於相關的應用程序,包括編輯器和 XML 處理工具,它工作得很好,但是對於一般用途的應用程序,它的表現不是最理想的。

  使用 DOM 模型最主要的問題是,它迫使您將 XML 代碼分散到整個應用程序中。更好的辦法是將 XML 代碼集中到一兩個包中。

  我回想起一個項目,我曾經審查過圍繞著 DOM 樹建立的相當大的產品。在這個應用程序中,差不多每個類都要從 XML 樹中提取數據或者向此樹中插入數據。如圖 1 所示,可以看到基本上每個包都依賴於 DOM 樹。

  圖 1. 該模型中每個包都依賴於 DOM

  使用 XML: 安全編碼實踐(2)

  事件證明,由於多種原因,這樣做難以調試、難以處理、難以維護:

  從字符串到原生類型的轉換不統一。驗證不一致,不同的例程以不同的方式解釋同一 DOM 樹。

  DOM 中的信息不是理想的格式,造成算法非常復雜。

  難以跟蹤變化。一個例程更新 DOM 樹後,可能造成其他例程不能訪問同一數據。

  XML 詞匯表中的任何變化都需要團隊成員檢查成千上萬行代碼。

  調試極其困難,無人知道哪一個例程造成了 DOM 樹中的哪一種結果。

  對於 SQL 數據庫,分解從對象模型中加載的數據是一種好辦法。這一點同樣適用於 XML。

  圖 2 示范了一種更健壯的替代方案,它依靠應用程序定義的對象模型,將處理 XML 的代碼隔離在 Serialization 包中。

  圖 2. 隔離 XML 代碼

  使用 XML: 安全編碼實踐(2)

  通過這個專用的包,可以將模型裝入內存,或者將它序列化為 XML。這樣做的好處包括:

  在應用程序精心定義的某一部分中封裝(隔離)處理 XML 的代碼。可能仍然涉及到成千上萬行代碼,不過可以單獨進行測試。

  有利於更一致地使用 XML,因為 XML 代碼由一名開發人員負責(我也曾遇到過需要整個團隊的大規模 XML 代碼)。

  加載的時候,可以重新組織數據,以便更好地適應算法。比如,加載到散列表或者數據庫中可以幫助處理大型數據集。

  更容易采用模型/視圖/控制器(MVC)范型。

  對象模型從 XML 模型中解耦出來,可以獨立演化。

  顯然,序列化例程可能還要使用 DOM 實際進行解析(雖然我傾向於認為 SAX 更有效)。還可以看看 JAXB 或 Castor,它們基本上自動生成序列化包。

  上述好處的代價是什麼呢?一開始的代價要高一些,不過您很快就會發現,封裝 XML 代碼的時候節省了時間。在開發的第一個階段,通常都希望 XML 詞匯表能夠較快地演化。即使在這個階段,也會看到將這些變動局部化所帶來的好處。

  結束語

  本文給我們的啟示是:一點點深謀遠慮可以帶來極大的回報。花一點時間來設計應用程序和隔離 XML 組件吧。

  下一期文章將回顧十分重要的驗證問題。

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved