歡迎您光臨本站 註冊首頁

XML:GWT 與 PHP 之間的橋樑

←手機掃碼閱讀     火星人 @ 2014-03-12 , reply:0
  
Google Web Toolkit(GWT)應用程序除了以傳統的 Java™ 方式連接到 servlet 外,還可以使用 PHP Web 服務發送和接收 XML 格式的數據。您將探索以 Java 和 PHP 語言生成和處理 XML 文檔的方法。

通過 GWT 可以方便地訪問用 Java 語言編寫的伺服器端 servlet,並且客戶機和伺服器之間的數據傳遞是透明的。但是,當使用 GWT 時,不僅可以與那些 servlet 通信,還可以隨意地與所有類型的 Web 服務交換數據。在很多情況下(對於簡單的服務),可以用純文本格式傳輸數據,但是每當遇到結構化的數據或較複雜的數據(例如 RSS)時,數據很可能是用 XML 表示的。

常用縮寫詞
  • Ajax:Asynchronous JavaScript + XML
  • PEAR:PHP Extension and Application Repository
  • RPC:遠程過程調用(Remote procedure call)
  • RSS:真正簡單的聚合(Really Simple Syndication)
  • W3C:萬維網聯盟(World Wide Web Consortium)
  • XML:可擴展標記語言(Extensible Markup Language)

本文研究一個簡單的 GWT 應用程序和兩個 PHP Web 服務,展示生成和使用 XML 文檔的幾種不同的方法。本文無意成為詳盡的教程或手冊,而是提供一些忠告,使您能更快地開始使用 XML 作為連接 GWT 與 PHP 的橋樑。

測試應用程序

為了展示如何使用 XML 作為 PHP 與 GWT 之間的橋樑,我提供一個簡單的應用程序,這個應用程序基於國家/地區/城市數據。查看 清單 1 中的資料庫創建代碼,可以看到:

  • 國家(country)有惟一的代碼(例如 UY 表示烏拉圭)和名稱。
  • 國家 被劃分為多個地區(region),地區有(國家內惟一的)代碼和名稱。
  • 地區 內有多個城市(city),城市有(純 ASCII)名稱、別名(可能包括外來字元)、人口(如果未知則為 0)、緯度和經度。城市名可出現在同一個國家的不同地區。
JSON:另一種可行的替代品

JavaScript Object Notation(JSON)最初是 JavaScript™ 語言的一部分,但是最終發展為 XML 的成熟、有效的替代品。JSON 提供一種簡單、可讀、基於文本的格式,用於表示數組和對象。而且,可以肯定地說,對於同樣的數據,數據的 XML 表示和 JSON 表示在大小上相差無幾。一些著名的站點(例如 Google 或 Yahoo!)同時提供 JSON 和 XML。

JSON 的優勢在於,JavaScript 可以非常快地處理它(例如,它可以只用一條語句就將 JSON 轉換成一個對象),這使得它很受 Web 開發人員的歡迎。由於 GWT 將所有客戶端代碼編譯為 JavaScript 代碼,顯然 GWT 為它提供了一個很好的庫,本文中的所有例子也可以用 JSON 而不是 XML 編寫。請參閱 參考資料 小節,獲得關於 JSON 的更多信息的鏈接。


清單 1. 資料庫創建代碼
				  CREATE DATABASE world      DEFAULT CHARACTER SET latin1      COLLATE latin1_general_ci;    USE world;    CREATE TABLE countries (      countryCode char(2) NOT NULL,      countryName varchar(50) NOT NULL,      PRIMARY KEY (countryCode)      KEY countryName (countryName)      );    CREATE TABLE regions (      countryCode char(2) NOT NULL,      regionCode char(2) NOT NULL,      regionName varchar(50) NOT NULL,      PRIMARY KEY (countryCode,regionCode),      KEY regionName (regionName)      );    CREATE TABLE cities (      countryCode char(2) NOT NULL,      cityName varchar(50) NOT NULL,      cityAccentedName varchar(50) NOT NULL,      regionCode char(2) NOT NULL,      population bigint(20) NOT NULL,      latitude float(10,7) NOT NULL,      longitude float(10,7) NOT NULL,      KEY `INDEX` (countryCode,regionCode,cityName),      KEY cityName (cityName),      KEY cityAccentedName (cityAccentedName)      );  

我創建了一個簡單的 GWT 項目,它只有一個表單和兩個 PHP Web 服務。(下載 小節提供了完整的源代碼)。啟動該應用程序時,可以看到 圖 1 中的窗口。


圖 1. 空的表單

GWT 表單允許輸入一個城市名的一部分,然後調用一個 PHP 服務獲得與輸入的內容匹配的所有城市。這些城市顯示在一個網格中,可以編輯人口(population)、緯度(latitude)和經度(longitude)欄位。然後,可以將編輯的數據發回到另一個 PHP Web 服務,後者將更新資料庫。所有數據傳輸都是通過 XML 進行的。2009 年是查爾斯達爾文 200 誕辰和他的著作物種起源 誕生 150 周年,您可以查看城市名中包含 DARWIN 的城市;圖 2 顯示了結果。


圖 2. 搜索城市名中包含 “Darwin” 的城市

一些額外的配置

下面是我使用的軟體,僅供參考:

  • GWT version 1.5.3
  • PHP version 5.2.8
  • MySQL® database server version 5.0.67
  • Apache version 2.2.10 under OpenSUSE® version 11.1

我安裝的所有軟體都是開箱即用的,但是 GWT 要求一個額外的配置步驟,這樣才能測試 GWT-PHP 連接;請參閱側邊欄 SOP 問題,看看是什麼原因。為了禁用內部 GWT 瀏覽器的同源策略(same-origin policy,SOP)檢查,可以編輯 GWT 目錄中的 ./mozilla-1.7.12/greprefs/all.js 文件,在文件的最後添加 清單 2 中的代碼行:


清單 2. 修改內部 GWT 瀏覽器的配置
				  pref("capability.policy.default.XMLHttpRequest.abort", "allAccess");  pref("capability.policy.default.XMLHttpRequest.getAllResponseHeaders","allAccess");  pref("capability.policy.default.XMLHttpRequest.getResponseHeader","allAccess");  pref("capability.policy.default.XMLHttpRequest.open", "allAccess");  pref("capability.policy.default.XMLHttpRequest.send", "allAccess");  pref("capability.policy.default.XMLHttpRequest.setRequestHeader","allAccess");  pref("capability.policy.default.XMLHttpRequest.onreadystatechange","allAccess");  pref("capability.policy.default.XMLHttpRequest.readyState", "allAccess");  pref("capability.policy.default.XMLHttpRequest.responseText","allAccess");  pref("capability.policy.default.XMLHttpRequest.responseXML","allAccess");  pref("capability.policy.default.XMLHttpRequest.status", "allAccess");  pref("capability.policy.default.XMLHttpRequest.statusText", "allAccess");  

每當更新 GWT 時,需要再次作出這樣的更改。而且,請注意,如果不這樣做,編寫的代碼在 Hosted 模式下會失敗,但是在 compiled 模式下卻可以正確運行。作出以上更改后,您的代碼可能在 Hosted 模式下可以運行,但是在 Compiled 模式下卻會失敗,所以要小心!

SOP 問題

SOP 是一種安全性約束,它實際上禁止從某個源(即 URL 的協議/主機/埠三元組)裝載的頁面訪問來自不同源的數據。(Windows® Internet Explorer® 對待 SOP 問題非常隨便,它會忽略埠的變化,但這並不是標準)。例如,如果 GWT Web 客戶機是從 http://www.yoursite.com:80/some/page/at/your/site 裝載的,那麼 SOP 將不允許客戶機獲取來自該 URL 之外的數據,並阻塞對 https://www.yoursite.com(不同的協議)、http://othersite.com(不同的主機)甚至是 http://www.yoursite.com:81(不同的埠)的調用。

SOP 是一種很好的思想,因為它可以徹底防止來自某個源的惡意 JavaScript 代碼訪問和操縱從另一個源獲得的數據。禁用 SOP 實際上是釣魚者真正想要的:當您查看一個合法的、正當的頁面時,卻有一個第三方在監視它。如果有了 SOP,就可以確保您查看的所有內容都是由預期的源發送的;不可能存在來自其他(可能是可疑的)源的代碼。

相反,對於 GWT 開發人員來說,SOP 相當麻煩。當在 Hosted 模式下測試應用程序時,它連接到埠 8888,但是要訪問的 PHP 服務卻在標準的 80 埠中,所以 SOP 將拒絕調用。(當然,在 Compiled 模式下部署應用程序之後,它可以完美地運行,因為它也是從標準的 80 埠運行的,剛好符合 SOP)。您不想訪問所有類型的站點,只是想訪問同一個源的不同埠,但是 SOP 就是攔著您。

用 PHP 發送 XML

這個應用程序只定義一個簡單的表單,其中有一些標籤、一個文本框、兩個命令按鈕和一個用於顯示結果的網格。每當單擊 Get cities 時,該應用程序調用一個 PHP 服務,以便獲得一個 XML 文檔,其中包含與文本框中輸入的內容匹配的所有城市。清單 3 顯示從 PHP 服務發送到 GWT 應用程序的一個示例 XML(有所簡化)。生成的 XML 代碼要用於演示一些 XML 功能,所以比用於真正的應用程序的 XML 要長得多。 通常,設計良好的 XML 服務使用更少的標記和更多的屬性,避免縮進,並且更短一些。


清單 3. 搜索 “tokyo” 時生成的 XML 文檔
				  <?xml version="1.0" encoding="UTF-8"?>   <cities>    <city name="tokyo">     <country code="JP" name="Japan"/>     <region code="40" name="Tokyo"/>     <coords>      <lat>35.6850014</lat>      <lon>139.7513885</lon>     </coords>     <pop>31480498</pop>    </city>    <city name="tokyo">     <country code="PG" name="Papua New Guinea"/>      <region code="01" name="Central"/>     <coords>      <lat>-8.3999996</lat>      <lon>147.1499939</lon>     </coords>    </city>    <city name="tokyojitori">     <country code="KR" name="Korea, Republic of"/>     <region code="16" name="Cholla-namdo"/>     <coords>      <lat>34.2380562</lat>      <lon>125.9394455</lon>     </coords>    </city>  </cities>  

這個 PHP 服務本身有兩個版本 — getcities1.php 和 getcities2.php —,每個版本展示生成 XML 的不同方法。

到目前為止,生成 XML 的最簡單的方法是連續輸出適當的文本,或者構建一個字元串,然後使用 echo 發出它。這裡應該將 content type 設為 text/xml,使之能夠被正確地識別,並且還要記得包括一個適當的 description 行,以指定 XML 版本和數據編碼方式。另外還必須轉義字元串,使字元串中不包括小於(<)、大於(>)或和(&)字元。最簡單的方法是使用 htmlspecialchars() PHP 函數。代碼很容易編寫,如 清單 4 所示。注意,縮進和換行實際上是不需要的,不過這樣做可以讓代碼更易於閱讀。


清單 4. 從 PHP 服務生成 XML 的最簡單的方法
				  ...  header("Content-type: text/xml");  ...  echo '<?xml version="1.0" encoding="UTF-8"?>'."\n";  echo '<cities>'."\n";  ...  while ($row= mysql_fetch_assoc($result)) {    echo ' <city name="'.htmlspecialchars($row['cityName']).'">'."\n";    echo '  <country code="'.$row['countryCode'].'" ';    echo 'name="'.htmlentities($row['countryName']).'"/>'."\n";    echo '  <region code="'.$row['regionCode'].'" ';    echo 'name="'.htmlentities($row['regionName']).'"/>'."\n";      echo '  <coords>'."\n";    echo '    <lat>'.$row['latitude'].'</lat>'."\n";    echo '    <lon>'.$row['longitude'].'</lon>'."\n";    echo '  </coords>'."\n";      if ($row['population']>0) {      echo '  <pop>'.$row['population'].'</pop>'."\n";    }      echo ' </city>'."\n";  }  echo '</cities>'."\n";  

生成 XML 代碼的另一種方法(也不是很長)是使用 XMLWriter。(與之成對的另一個類 XMLReader 可用於 XML 處理)。現在可以不用關心字元的轉義,因為這是自動完成的。雖然這種方法看起來比前面的 echo() 方法冗長一點,但是這種方法編寫的代碼更易於理解。特別注意,這裡使用了 php://output 協議,以便用 echo 命令發出文本。(如 清單 5 所示)。


清單 5. XMLWriter 提供逐個元素地構建 XML 文檔的簡單方法
				  ...  $writer= new XMLWriter();  $writer->openURI('php://output')  $writer->startDocument('1.0', 'UTF-8');  $writer->startElement("cities");    while ($row= mysql_fetch_assoc($result)) {    $writer->startElement("city");    $writer->writeAttribute("name", $row['cityName']);      $writer->startElement("country");    $writer->writeAttribute("code", $row['countryCode']);    $writer->writeAttribute("name", $row['countryName']);    $writer->endElement();      $writer->startElement("region");    $writer->writeAttribute("code", $row['regionCode']);    $writer->writeAttribute("name", $row['regionName']);    $writer->endElement();      $writer->startElement("coords");    $writer->writeElement("lat", $row['latitude']);    $writer->writeElement("lon", $row['longitude']);    $writer->endElement();      if ($row['population']>0) {      $writer->writeElement("pop", $row['population']);    }      $writer->endElement();	// city  }  $writer->endElement(); // cities  ...  

如果想體驗更多生成 XML 的方法,PHP 當然提供了很多的選擇。例如,可以使用 SimpleXML;以後您將使用它讀取 XML,但是它還提供 XML 文檔創建功能。另外,還可以看一下 PEAR 框架,它包括一些可用於輕鬆生成 XML 的類。(請參閱 參考資料,獲得更多信息的鏈接)。

用 GWT 處理 XML

GWT 只提供 XMLParser(在 com.google.gwt.xml.client 包中)用於讀、寫 XML。可以使用 parse() 方法創建一個 Document,然後使用 getDocumentElement() 獲得它的根元素;然後,便可以開始處理 XML。

注意,應該使用 removeWhitespace() 方法去掉文檔中的空白。瀏覽器解析器有時候會創建與製表符或換行符對應的空文本節點,如果不去掉它們,處理過程中會遇到不相關的、意外的元素,這樣會破壞邏輯(見 清單 6)。另外還有一點要注意:如果預期有 CDATA 區段,那麼需要檢查瀏覽器是否接受 supportsCDATASection() 方法;如果不接受,那些區段將產生文本節點。請參閱 GWT 文檔(見 參考資料),獲得更多這方面的信息。


清單 6. GWT 中提供了 XMLParser 用於讀取和創建 XML
				  protected void loadCities(final String xmlCities) {    ...    final Document xmlDoc= XMLParser.parse(xmlCities);    final Element root= xmlDoc.getDocumentElement();    XMLParser.removeWhitespace(xmlDoc);      final NodeList cities= root.getElementsByTagName("city");    for (int i= 0; i < cities.getLength(); i++) {      final Element city= (Element)cities.item(i);      // show city.getAttributeNode("name").getValue()        final Element country= (Element)city.getElementsByTagName("country").item(0);      // show country.getAttributeNode("code").getValue()      // show country.getAttributeNode("name").getValue()      ...      final Element population= (Element)city.getElementsByTagName("pop").item(0);      if (population != null) {        // show population.getFirstChild().getNodeValue()      }        final Element coords= (Element)city.getElementsByTagName("coords").item(0);      final Element lat= (Element)coords.getElementsByTagName("lat").item(0);      // show lat.getFirstChild().getNodeValue()      ...    }  ...  

要獲得重複的元素(例如這個例子中的 city),可以使用 getElementsByTagName() 方法,并迭代生成的數組。另一種方法是使用 getFirstChild() 方法,然後使用 getNextSibling() 遍歷同一級別上的其他元素。要獲得屬性,首先需要使用 getAttributeNode() 方法,然後使用 getValue() 方法。還有一些用於處理 CDATA 區段、註釋和所有可能的 XML 組件的方法。

用 GWT 發送 XML

GWT 應用程序讓用戶編輯人口、緯度和經度欄位,然後將城市數據發回到伺服器,以更新資料庫。有兩種演算法:一種是簡單的演算法,這種演算法逐塊構建 XML 字元串,還有一種基於 XMLParser 的演算法,該演算法使用特定的方法創建所需的結構。

getCities1() 方法中顯示了較簡單的演算法。可以使用 String 或 StringBuffer 對象構建 XML。我使用了前者,因為它使代碼更加清晰;但是如果考慮性能,後者很可能更好一些。這裡也存在和 PHP 版本中一樣的字元串轉義問題,所以使用 Html.htmlspecialchars() 方法修改該問題。(見 清單 7,為了看起來更清晰,稍微作了修改)。


清單 7. 使用字元串逐塊構建 XML 很簡單
				  protected String getCities1() {    String result= "";      result+= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";    result+= "<cities>\n";      for (all rows in the grid) {      // get cityName, countryCode, regionCode, pop, lat, and lon, from the grid        result+= " <city name=\"" + Html.htmlspecialchars(cityName) + "\">\n";      result+= "  <country code=\"" + countryCode + "\"/>\n";      result+= "  <region code=\"" + regionCode + "\"/>\n";      if (!pop.equals("0") && !pop.isEmpty()) {        result+= "  <pop>" + pop + "</pop>\n";      }        result+= "  <coords>\n";      result+= "   <lat>" + lat + "</lat>\n";      result+= "   <lon>" + lon + "</lon>\n";      result+= "  </coords>\n";      result+= "</city>\n";    }    result+= "</cities>\n";    return result;  }  

另一種創建 XML 的簡單演算法是 XMLParser 類的 createDocument() 方法。這種演算法的風格很容易讓人想起 PHP 中的 SimpleXML 函數,首先創建一個空文檔,然後向其中添加元素。可以創建所有類型的節點,並設置屬性值。最後,標準的 Java toString() 方法生成對象的一個表示。您只需添加初始版本和編碼行,就可以得到需要的字元串。(見 清單 8,為了看起來更清晰,代碼有所修改和刪減)。


清單 8. 創建 XML 的 XMLParser 方法類似於 PHP 中的 SimpleXML 方法
				  protected String getCities2() {    Document xml= XMLParser.createDocument();    Element cities= xml.createElement("cities");    xml.appendChild(cities);      for (all rows in the grid) {      // get cityName, countryCode, regionCode, pop, lat, and lon, from the grid        Element city= xml.createElement("city");      city.setAttribute("name", cityName);        Element country= xml.createElement("country");      country.setAttribute("code", countryCode);      city.appendChild(country);      ...      if (!pop.equals("0") && !pop.isEmpty()) {        Element popEl= xml.createElement("pop");        Text popText= xml.createTextNode(pop);        popEl.appendChild(popText);        city.appendChild(popEl);      }        Element coords= xml.createElement("coords");      Element lat= xml.createElement("lat");      Text latText= xml.createTextNode(lat);      lat.appendChild(latText);      coords.appendChild(lat);      ...      city.appendChild(coords);      cities.appendChild(city);    }    return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + xml.toString();  }  

在 PHP 中讀取 XML

在 PHP 中處理 XML 是一個老問題,有很多的解決方案。但是,我認為,從清晰和簡潔的角度看,沒有哪種方案能比得上 SimpleXML 。基本上,首先通過將 XML 文檔提供給 simplexml_load_string() 方法創建一個 PHP 對象,然後使用標準的 PHP 操作符遍歷結果。屬性變成數組中的元素(例如,清單 9 展示了如何讀取國家代碼),而元素則可以作為一個數組(通過使用 children() 方法)或作為對象的屬性來訪問。


清單 9. SimpleXML 是目前在 PHP 中進行 XML 處理的最容易的方法
				  $xml_str= 
Google Web Toolkit(GWT)應用程序除了以傳統的 Java™ 方式連接到 servlet 外,還可以使用 PHP Web 服務發送和接收 XML 格式的數據。您將探索以 Java 和 PHP 語言生成和處理 XML 文檔的方法。

通過 GWT 可以方便地訪問用 Java 語言編寫的伺服器端 servlet,並且客戶機和伺服器之間的數據傳遞是透明的。但是,當使用 GWT 時,不僅可以與那些 servlet 通信,還可以隨意地與所有類型的 Web 服務交換數據。在很多情況下(對於簡單的服務),可以用純文本格式傳輸數據,但是每當遇到結構化的數據或較複雜的數據(例如 RSS)時,數據很可能是用 XML 表示的。

常用縮寫詞
  • Ajax:Asynchronous JavaScript + XML
  • PEAR:PHP Extension and Application Repository
  • RPC:遠程過程調用(Remote procedure call)
  • RSS:真正簡單的聚合(Really Simple Syndication)
  • W3C:萬維網聯盟(World Wide Web Consortium)
  • XML:可擴展標記語言(Extensible Markup Language)

本文研究一個簡單的 GWT 應用程序和兩個 PHP Web 服務,展示生成和使用 XML 文檔的幾種不同的方法。本文無意成為詳盡的教程或手冊,而是提供一些忠告,使您能更快地開始使用 XML 作為連接 GWT 與 PHP 的橋樑。

測試應用程序

為了展示如何使用 XML 作為 PHP 與 GWT 之間的橋樑,我提供一個簡單的應用程序,這個應用程序基於國家/地區/城市數據。查看 清單 1 中的資料庫創建代碼,可以看到:

  • 國家(country)有惟一的代碼(例如 UY 表示烏拉圭)和名稱。
  • 國家 被劃分為多個地區(region),地區有(國家內惟一的)代碼和名稱。
  • 地區 內有多個城市(city),城市有(純 ASCII)名稱、別名(可能包括外來字元)、人口(如果未知則為 0)、緯度和經度。城市名可出現在同一個國家的不同地區。
JSON:另一種可行的替代品

JavaScript Object Notation(JSON)最初是 JavaScript™ 語言的一部分,但是最終發展為 XML 的成熟、有效的替代品。JSON 提供一種簡單、可讀、基於文本的格式,用於表示數組和對象。而且,可以肯定地說,對於同樣的數據,數據的 XML 表示和 JSON 表示在大小上相差無幾。一些著名的站點(例如 Google 或 Yahoo!)同時提供 JSON 和 XML。

JSON 的優勢在於,JavaScript 可以非常快地處理它(例如,它可以只用一條語句就將 JSON 轉換成一個對象),這使得它很受 Web 開發人員的歡迎。由於 GWT 將所有客戶端代碼編譯為 JavaScript 代碼,顯然 GWT 為它提供了一個很好的庫,本文中的所有例子也可以用 JSON 而不是 XML 編寫。請參閱 參考資料 小節,獲得關於 JSON 的更多信息的鏈接。


清單 1. 資料庫創建代碼
				  CREATE DATABASE world      DEFAULT CHARACTER SET latin1      COLLATE latin1_general_ci;    USE world;    CREATE TABLE countries (      countryCode char(2) NOT NULL,      countryName varchar(50) NOT NULL,      PRIMARY KEY (countryCode)      KEY countryName (countryName)      );    CREATE TABLE regions (      countryCode char(2) NOT NULL,      regionCode char(2) NOT NULL,      regionName varchar(50) NOT NULL,      PRIMARY KEY (countryCode,regionCode),      KEY regionName (regionName)      );    CREATE TABLE cities (      countryCode char(2) NOT NULL,      cityName varchar(50) NOT NULL,      cityAccentedName varchar(50) NOT NULL,      regionCode char(2) NOT NULL,      population bigint(20) NOT NULL,      latitude float(10,7) NOT NULL,      longitude float(10,7) NOT NULL,      KEY `INDEX` (countryCode,regionCode,cityName),      KEY cityName (cityName),      KEY cityAccentedName (cityAccentedName)      );  

我創建了一個簡單的 GWT 項目,它只有一個表單和兩個 PHP Web 服務。(下載 小節提供了完整的源代碼)。啟動該應用程序時,可以看到 圖 1 中的窗口。


圖 1. 空的表單

GWT 表單允許輸入一個城市名的一部分,然後調用一個 PHP 服務獲得與輸入的內容匹配的所有城市。這些城市顯示在一個網格中,可以編輯人口(population)、緯度(latitude)和經度(longitude)欄位。然後,可以將編輯的數據發回到另一個 PHP Web 服務,後者將更新資料庫。所有數據傳輸都是通過 XML 進行的。2009 年是查爾斯達爾文 200 誕辰和他的著作物種起源 誕生 150 周年,您可以查看城市名中包含 DARWIN 的城市;圖 2 顯示了結果。


圖 2. 搜索城市名中包含 “Darwin” 的城市

一些額外的配置

下面是我使用的軟體,僅供參考:

  • GWT version 1.5.3
  • PHP version 5.2.8
  • MySQL® database server version 5.0.67
  • Apache version 2.2.10 under OpenSUSE® version 11.1

我安裝的所有軟體都是開箱即用的,但是 GWT 要求一個額外的配置步驟,這樣才能測試 GWT-PHP 連接;請參閱側邊欄 SOP 問題,看看是什麼原因。為了禁用內部 GWT 瀏覽器的同源策略(same-origin policy,SOP)檢查,可以編輯 GWT 目錄中的 ./mozilla-1.7.12/greprefs/all.js 文件,在文件的最後添加 清單 2 中的代碼行:


清單 2. 修改內部 GWT 瀏覽器的配置
				  pref("capability.policy.default.XMLHttpRequest.abort", "allAccess");  pref("capability.policy.default.XMLHttpRequest.getAllResponseHeaders","allAccess");  pref("capability.policy.default.XMLHttpRequest.getResponseHeader","allAccess");  pref("capability.policy.default.XMLHttpRequest.open", "allAccess");  pref("capability.policy.default.XMLHttpRequest.send", "allAccess");  pref("capability.policy.default.XMLHttpRequest.setRequestHeader","allAccess");  pref("capability.policy.default.XMLHttpRequest.onreadystatechange","allAccess");  pref("capability.policy.default.XMLHttpRequest.readyState", "allAccess");  pref("capability.policy.default.XMLHttpRequest.responseText","allAccess");  pref("capability.policy.default.XMLHttpRequest.responseXML","allAccess");  pref("capability.policy.default.XMLHttpRequest.status", "allAccess");  pref("capability.policy.default.XMLHttpRequest.statusText", "allAccess");  

每當更新 GWT 時,需要再次作出這樣的更改。而且,請注意,如果不這樣做,編寫的代碼在 Hosted 模式下會失敗,但是在 compiled 模式下卻可以正確運行。作出以上更改后,您的代碼可能在 Hosted 模式下可以運行,但是在 Compiled 模式下卻會失敗,所以要小心!

SOP 問題

SOP 是一種安全性約束,它實際上禁止從某個源(即 URL 的協議/主機/埠三元組)裝載的頁面訪問來自不同源的數據。(Windows® Internet Explorer® 對待 SOP 問題非常隨便,它會忽略埠的變化,但這並不是標準)。例如,如果 GWT Web 客戶機是從 http://www.yoursite.com:80/some/page/at/your/site 裝載的,那麼 SOP 將不允許客戶機獲取來自該 URL 之外的數據,並阻塞對 https://www.yoursite.com(不同的協議)、http://othersite.com(不同的主機)甚至是 http://www.yoursite.com:81(不同的埠)的調用。

SOP 是一種很好的思想,因為它可以徹底防止來自某個源的惡意 JavaScript 代碼訪問和操縱從另一個源獲得的數據。禁用 SOP 實際上是釣魚者真正想要的:當您查看一個合法的、正當的頁面時,卻有一個第三方在監視它。如果有了 SOP,就可以確保您查看的所有內容都是由預期的源發送的;不可能存在來自其他(可能是可疑的)源的代碼。

相反,對於 GWT 開發人員來說,SOP 相當麻煩。當在 Hosted 模式下測試應用程序時,它連接到埠 8888,但是要訪問的 PHP 服務卻在標準的 80 埠中,所以 SOP 將拒絕調用。(當然,在 Compiled 模式下部署應用程序之後,它可以完美地運行,因為它也是從標準的 80 埠運行的,剛好符合 SOP)。您不想訪問所有類型的站點,只是想訪問同一個源的不同埠,但是 SOP 就是攔著您。

用 PHP 發送 XML

這個應用程序只定義一個簡單的表單,其中有一些標籤、一個文本框、兩個命令按鈕和一個用於顯示結果的網格。每當單擊 Get cities 時,該應用程序調用一個 PHP 服務,以便獲得一個 XML 文檔,其中包含與文本框中輸入的內容匹配的所有城市。清單 3 顯示從 PHP 服務發送到 GWT 應用程序的一個示例 XML(有所簡化)。生成的 XML 代碼要用於演示一些 XML 功能,所以比用於真正的應用程序的 XML 要長得多。 通常,設計良好的 XML 服務使用更少的標記和更多的屬性,避免縮進,並且更短一些。


清單 3. 搜索 “tokyo” 時生成的 XML 文檔
				  <?xml version="1.0" encoding="UTF-8"?>   <cities>    <city name="tokyo">     <country code="JP" name="Japan"/>     <region code="40" name="Tokyo"/>     <coords>      <lat>35.6850014</lat>      <lon>139.7513885</lon>     </coords>     <pop>31480498</pop>    </city>    <city name="tokyo">     <country code="PG" name="Papua New Guinea"/>      <region code="01" name="Central"/>     <coords>      <lat>-8.3999996</lat>      <lon>147.1499939</lon>     </coords>    </city>    <city name="tokyojitori">     <country code="KR" name="Korea, Republic of"/>     <region code="16" name="Cholla-namdo"/>     <coords>      <lat>34.2380562</lat>      <lon>125.9394455</lon>     </coords>    </city>  </cities>  

這個 PHP 服務本身有兩個版本 — getcities1.php 和 getcities2.php —,每個版本展示生成 XML 的不同方法。

到目前為止,生成 XML 的最簡單的方法是連續輸出適當的文本,或者構建一個字元串,然後使用 echo 發出它。這裡應該將 content type 設為 text/xml,使之能夠被正確地識別,並且還要記得包括一個適當的 description 行,以指定 XML 版本和數據編碼方式。另外還必須轉義字元串,使字元串中不包括小於(<)、大於(>)或和(&)字元。最簡單的方法是使用 htmlspecialchars() PHP 函數。代碼很容易編寫,如 清單 4 所示。注意,縮進和換行實際上是不需要的,不過這樣做可以讓代碼更易於閱讀。


清單 4. 從 PHP 服務生成 XML 的最簡單的方法
				  ...  header("Content-type: text/xml");  ...  echo '<?xml version="1.0" encoding="UTF-8"?>'."\n";  echo '<cities>'."\n";  ...  while ($row= mysql_fetch_assoc($result)) {    echo ' <city name="'.htmlspecialchars($row['cityName']).'">'."\n";    echo '  <country code="'.$row['countryCode'].'" ';    echo 'name="'.htmlentities($row['countryName']).'"/>'."\n";    echo '  <region code="'.$row['regionCode'].'" ';    echo 'name="'.htmlentities($row['regionName']).'"/>'."\n";      echo '  <coords>'."\n";    echo '    <lat>'.$row['latitude'].'</lat>'."\n";    echo '    <lon>'.$row['longitude'].'</lon>'."\n";    echo '  </coords>'."\n";      if ($row['population']>0) {      echo '  <pop>'.$row['population'].'</pop>'."\n";    }      echo ' </city>'."\n";  }  echo '</cities>'."\n";  

生成 XML 代碼的另一種方法(也不是很長)是使用 XMLWriter。(與之成對的另一個類 XMLReader 可用於 XML 處理)。現在可以不用關心字元的轉義,因為這是自動完成的。雖然這種方法看起來比前面的 echo() 方法冗長一點,但是這種方法編寫的代碼更易於理解。特別注意,這裡使用了 php://output 協議,以便用 echo 命令發出文本。(如 清單 5 所示)。


清單 5. XMLWriter 提供逐個元素地構建 XML 文檔的簡單方法
				  ...  $writer= new XMLWriter();  $writer->openURI('php://output')  $writer->startDocument('1.0', 'UTF-8');  $writer->startElement("cities");    while ($row= mysql_fetch_assoc($result)) {    $writer->startElement("city");    $writer->writeAttribute("name", $row['cityName']);      $writer->startElement("country");    $writer->writeAttribute("code", $row['countryCode']);    $writer->writeAttribute("name", $row['countryName']);    $writer->endElement();      $writer->startElement("region");    $writer->writeAttribute("code", $row['regionCode']);    $writer->writeAttribute("name", $row['regionName']);    $writer->endElement();      $writer->startElement("coords");    $writer->writeElement("lat", $row['latitude']);    $writer->writeElement("lon", $row['longitude']);    $writer->endElement();      if ($row['population']>0) {      $writer->writeElement("pop", $row['population']);    }      $writer->endElement();	// city  }  $writer->endElement(); // cities  ...  

如果想體驗更多生成 XML 的方法,PHP 當然提供了很多的選擇。例如,可以使用 SimpleXML;以後您將使用它讀取 XML,但是它還提供 XML 文檔創建功能。另外,還可以看一下 PEAR 框架,它包括一些可用於輕鬆生成 XML 的類。(請參閱 參考資料,獲得更多信息的鏈接)。

用 GWT 處理 XML

GWT 只提供 XMLParser(在 com.google.gwt.xml.client 包中)用於讀、寫 XML。可以使用 parse() 方法創建一個 Document,然後使用 getDocumentElement() 獲得它的根元素;然後,便可以開始處理 XML。

注意,應該使用 removeWhitespace() 方法去掉文檔中的空白。瀏覽器解析器有時候會創建與製表符或換行符對應的空文本節點,如果不去掉它們,處理過程中會遇到不相關的、意外的元素,這樣會破壞邏輯(見 清單 6)。另外還有一點要注意:如果預期有 CDATA 區段,那麼需要檢查瀏覽器是否接受 supportsCDATASection() 方法;如果不接受,那些區段將產生文本節點。請參閱 GWT 文檔(見 參考資料),獲得更多這方面的信息。


清單 6. GWT 中提供了 XMLParser 用於讀取和創建 XML
				  protected void loadCities(final String xmlCities) {    ...    final Document xmlDoc= XMLParser.parse(xmlCities);    final Element root= xmlDoc.getDocumentElement();    XMLParser.removeWhitespace(xmlDoc);      final NodeList cities= root.getElementsByTagName("city");    for (int i= 0; i < cities.getLength(); i++) {      final Element city= (Element)cities.item(i);      // show city.getAttributeNode("name").getValue()        final Element country= (Element)city.getElementsByTagName("country").item(0);      // show country.getAttributeNode("code").getValue()      // show country.getAttributeNode("name").getValue()      ...      final Element population= (Element)city.getElementsByTagName("pop").item(0);      if (population != null) {        // show population.getFirstChild().getNodeValue()      }        final Element coords= (Element)city.getElementsByTagName("coords").item(0);      final Element lat= (Element)coords.getElementsByTagName("lat").item(0);      // show lat.getFirstChild().getNodeValue()      ...    }  ...  

要獲得重複的元素(例如這個例子中的 city),可以使用 getElementsByTagName() 方法,并迭代生成的數組。另一種方法是使用 getFirstChild() 方法,然後使用 getNextSibling() 遍歷同一級別上的其他元素。要獲得屬性,首先需要使用 getAttributeNode() 方法,然後使用 getValue() 方法。還有一些用於處理 CDATA 區段、註釋和所有可能的 XML 組件的方法。

用 GWT 發送 XML

GWT 應用程序讓用戶編輯人口、緯度和經度欄位,然後將城市數據發回到伺服器,以更新資料庫。有兩種演算法:一種是簡單的演算法,這種演算法逐塊構建 XML 字元串,還有一種基於 XMLParser 的演算法,該演算法使用特定的方法創建所需的結構。

getCities1() 方法中顯示了較簡單的演算法。可以使用 String 或 StringBuffer 對象構建 XML。我使用了前者,因為它使代碼更加清晰;但是如果考慮性能,後者很可能更好一些。這裡也存在和 PHP 版本中一樣的字元串轉義問題,所以使用 Html.htmlspecialchars() 方法修改該問題。(見 清單 7,為了看起來更清晰,稍微作了修改)。


清單 7. 使用字元串逐塊構建 XML 很簡單
				  protected String getCities1() {    String result= "";      result+= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";    result+= "<cities>\n";      for (all rows in the grid) {      // get cityName, countryCode, regionCode, pop, lat, and lon, from the grid        result+= " <city name=\"" + Html.htmlspecialchars(cityName) + "\">\n";      result+= "  <country code=\"" + countryCode + "\"/>\n";      result+= "  <region code=\"" + regionCode + "\"/>\n";      if (!pop.equals("0") && !pop.isEmpty()) {        result+= "  <pop>" + pop + "</pop>\n";      }        result+= "  <coords>\n";      result+= "   <lat>" + lat + "</lat>\n";      result+= "   <lon>" + lon + "</lon>\n";      result+= "  </coords>\n";      result+= "</city>\n";    }    result+= "</cities>\n";    return result;  }  

另一種創建 XML 的簡單演算法是 XMLParser 類的 createDocument() 方法。這種演算法的風格很容易讓人想起 PHP 中的 SimpleXML 函數,首先創建一個空文檔,然後向其中添加元素。可以創建所有類型的節點,並設置屬性值。最後,標準的 Java toString() 方法生成對象的一個表示。您只需添加初始版本和編碼行,就可以得到需要的字元串。(見 清單 8,為了看起來更清晰,代碼有所修改和刪減)。


清單 8. 創建 XML 的 XMLParser 方法類似於 PHP 中的 SimpleXML 方法
				  protected String getCities2() {    Document xml= XMLParser.createDocument();    Element cities= xml.createElement("cities");    xml.appendChild(cities);      for (all rows in the grid) {      // get cityName, countryCode, regionCode, pop, lat, and lon, from the grid        Element city= xml.createElement("city");      city.setAttribute("name", cityName);        Element country= xml.createElement("country");      country.setAttribute("code", countryCode);      city.appendChild(country);      ...      if (!pop.equals("0") && !pop.isEmpty()) {        Element popEl= xml.createElement("pop");        Text popText= xml.createTextNode(pop);        popEl.appendChild(popText);        city.appendChild(popEl);      }        Element coords= xml.createElement("coords");      Element lat= xml.createElement("lat");      Text latText= xml.createTextNode(lat);      lat.appendChild(latText);      coords.appendChild(lat);      ...      city.appendChild(coords);      cities.appendChild(city);    }    return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + xml.toString();  }  

在 PHP 中讀取 XML

在 PHP 中處理 XML 是一個老問題,有很多的解決方案。但是,我認為,從清晰和簡潔的角度看,沒有哪種方案能比得上 SimpleXML 。基本上,首先通過將 XML 文檔提供給 simplexml_load_string() 方法創建一個 PHP 對象,然後使用標準的 PHP 操作符遍歷結果。屬性變成數組中的元素(例如,清單 9 展示了如何讀取國家代碼),而元素則可以作為一個數組(通過使用 children() 方法)或作為對象的屬性來訪問。


清單 9. SimpleXML 是目前在 PHP 中進行 XML 處理的最容易的方法
___FCKpd___8

在 PHP 中處理 XML 還有許多種方法,可以從 參考資料 小節找到相關的鏈接。

結束語

有很多方法可以使用 XML 作為 GWT 與 PHP 之間的橋樑,我只是略作探討,不過,本文給出的方法應該足以讓您邁出第一步。需要記住的要點是,GWT 不止局限於它自己的 RPC 方法,還可以與 XML 友好共存,輕鬆生成和使用 XML 文檔。 (責任編輯:A6)

POST["xmldata"]; $xml_obj= simplexml_load_string($xml_str); ... foreach($xml_obj->children() as $city) { $name= addslashes($city['name']); $country= $city->country['code']; $region= $city->region['code']; $pop= $city->pop; $lat= $city->coords->lat; $lon= $city->coords->lon; mysql_query("REPLACE INTO cities ". "(cityName, countryCode, regionCode, population, latitude, longitude) VALUES (". "'{$name}', '{$country}', '{$region}', '{$pop}', '{$lat}', '{$lon}')"); }

在 PHP 中處理 XML 還有許多種方法,可以從 參考資料 小節找到相關的鏈接。

結束語

有很多方法可以使用 XML 作為 GWT 與 PHP 之間的橋樑,我只是略作探討,不過,本文給出的方法應該足以讓您邁出第一步。需要記住的要點是,GWT 不止局限於它自己的 RPC 方法,還可以與 XML 友好共存,輕鬆生成和使用 XML 文檔。 (責任編輯:A6)



[火星人 ] XML:GWT 與 PHP 之間的橋樑已經有449次圍觀

http://coctec.com/docs/linux/show-post-68868.html