層級: 中級 Brett McLaughlin (brett@newInstance.com), 作家,編輯, O'Reilly Media Inc. 2006 年 12 月 21 日
在 本系列的上一篇文章 中,您看到了 Ajax 應用程式如何以 XML 格式化發往伺服器的請求。還瞭解了為什麼這在大多數情況下並不是一個好主意。這篇文章主要探討在大多數情況下確實是 好主意的一種做法:向客戶機返回 XML 響應。
我其實並不喜歡寫那種主要告訴您什麼不應該 做的文章。很多時候,那都會是一篇非常愚蠢的文章。我要在前半篇文章中解釋某些東西,然後在後半篇文章中說明使用您剛剛才學會的那種技術是一個多麼糟糕的主意。在很大程度上,上一期文章正是這樣一種情況(如果您錯過了那一期文章,請查看 參考資料 中的連結),那篇文章教您如何將 XML 作為 Ajax 應用程式的請求資料格式使用。 但願這篇文章能夠彌補您花費在學習 XML 請求上的時間。在 Ajax 應用程式中,使用 XML 作為發送資料的格式的理由很少,但使伺服器向 客戶機回傳 XML 的理由很多。因此,您在上一篇文章中學到的關於 XML 的知識最終將在這篇文章中體現出某些價值。 伺服器(有時)不能響應太多的請求 在深入鑽研從伺服器擷取 XML 響應的技術之前,您需要理解,為什麼說使伺服器發送 XML 來響應請求是一個好主意(以及這與客戶機發送 XML 請求不同的原因所在)。 客戶機以成對的名稱和數值發送請求 回憶一下上一篇文章,就會知道,在大多數情況下,客戶機不需要使用 XML,因為他們會使用成對的名稱和數值發送請求。因此,您可能會發送一個這樣的名稱:name=jennifer 。只需簡單地在連續的成對的名稱和數值之間添加一個 “與” 符號(& ),即可將其放在一起,就像這樣:name=jennifer&job=president 。使用簡單的文本和這些名稱值對,客戶機即可輕鬆向伺服器請求多個值。很少需要用到 XML 提供的額外結構(及其帶來的額外開銷)。 實際上,需要向伺服器發送 XML 的所有理由都差不多可以歸入以下兩個基本的類別中:
- 伺服器僅 接受 XML 請求。在這類情況下,您別無選擇。上一期文章中介紹的基礎知識應已使您掌握了發送此類請求所必需的工具。
- 您正在調用一個僅接受 XML 或 SOAP 請求的遠程 API。這實際上就是上一種情況的特例,但值得單獨拿出來提一下。如果您希望在一個非同步請求中使用來自 Google 或 Amazon 的 API,就會有一些特殊的考慮事項。在下一期的文章中,我將介紹這些考慮事項,還會給出一些向 API 發送此類請求的樣本。
伺服器無法(以一種標準方式)發送成對的名稱和數值 在您發送成對的名稱和數值時,網頁瀏覽器會發送請求,平台會響應該請求,並承載一個伺服器程式,配合它將那些成對的名稱和數值轉換成伺服器程式可以輕鬆處理的資料。實際上,每一種伺服器端技術 —— 從 Java servlet 到 PHP、再到 Perl、Ruby on Rails —— 都允許您調用多種方法來根據名稱擷取值。因此,擷取 name 屬性只是小事一樁。 這種情況並不會將我們引向另外一個方向。如果伺服器使用字串 name=jennifer&job=president 應答一個應用程式,客戶機沒有任何標準化的簡便方法來將每個對拆分成名稱和值。您必須手動解析所返回的資料。如果伺服器返回一個由成對的名稱和數值構成的響應,這樣的響應的解釋難度與使用分號、豎線或其他任何非標準格式化字元相同。
|
給我一點空間! 在絕大多數 HTTP 要求中,逸出序列 %20 用於表示一個空格,文本 “Live Together, Die Alone” 將以 Live%20Together,%20Die%20Alone 的形式通過 HTTP 發送。 |
|
對於您來說,這就意味沒有任何簡單的方法在響應中使用純文字、使客戶機以一種標準的方法擷取並解釋響應,至少在響應包含多個值時是如此。假設您的伺服器只是要發回數字 42,那麼純文字是很好的選擇。但如果伺服器要一次性發回電視劇 Lost, Alias 和 Six Degrees 的近期收視率又該怎麼辦呢?儘管可以選擇許多種方法來使用純文字發送這一響應(清單 1 給出了一些樣本),但沒有一種是不需客戶機進行某些處理的極其簡單的方法,也沒有一種是標準化的方法。 清單 1. 收視率的伺服器響應(不同版本)
show=Alias&ratings=6.5|show=Lost&ratings=14.2|show=Six%20Degrees&ratings=9.1Alias=6.5&Lost=14.2&Six%20Degrees=9.1Alias|6.5|Lost|14.2|Six%20Degrees|9.1 |
儘管不難找到拆分這些響應字串的方法,但客戶機將不得不根據分號、等號、豎線和與符號解析並拆分這些字串。這不是編寫使其他開發人員能夠輕鬆理解和維護的健壯代碼的方法。 進入 XML 意識到沒有任何標準的方法可以使伺服器使用成對的名稱和數值響應客戶機之後,使用 XML 的原因也就顯而易見了。向客戶機發送資料時,成對的名稱和數值是非常好的選擇,因為伺服器和伺服器端語言可以輕鬆解釋成對的名稱和數值;向客戶機返回資料時使用 XML 也是如此。在本系列前幾期的文章中,您已經看到了利用 DOM 來解析 XML,在後續的文章中,還會看到 JSON 怎樣提供瞭解析 XML 的另一種選擇。在所有這一切之上,您可以將 XML 作為純文字處理,並以這種方式擷取其值。因此,有幾種方法可從伺服器獲得 XML 響應,並使用較為標準的代碼提取資料,在客戶機中使用這些資料。 還有一個額外的好處,XML 非常易於理解。比如說,大多數編寫程式的人都能理解 清單 2 中的資料。 清單 2. 收視率的伺服器響應(XML 格式)
<ratings> <show> <title>Alias</title> <rating>6.5</rating> </show> <show> <title>Lost</title> <rating>14.2</rating> </show> <show> <title>Six Degrees</title> <rating>9.1</rating> </show></ratings> |
在特定分號或撇號的含義方面,清單 2 中的代碼沒有任何隱晦之處。
從伺服器接收 XML 由於本系列的重點在於 Ajax 應用模式的用戶端,因此我不會深入探討關於伺服器端程式如何才能產生 XML 響應的細枝末節。但在您的客戶機接收 XML 時,您需要瞭解一些特殊的考慮事項。 首先,您可使用兩種基本的方式處理一個來自伺服器的 XML 響應:
- 作為碰巧被格式化為 XML 的純文字
- 作為一個 XML 文檔,由一個 DOM
Document 對象表示。
其次,舉例來說,假設有一個來自伺服器的簡單 XML 響應。清單 3 展示了與上面介紹的內容相同的收視率程式清單(實際上,是與 清單 2 相同的 XML,在這裡再次給出只是為了使您便於查看)。我將在這部分的討論中使用這段樣本 XML。 清單 3. XML 格式的收視率樣本
<ratings> <show> <title>Alias</title> <rating>6.5</rating> </show> <show> <title>Lost</title> <rating>14.2</rating> </show> <show> <title>Six Degrees</title> <rating>9.1</rating> </show></ratings> |
將 XML 作為純文字處理 處理 XML 的最簡單的選擇(至少就學習新的編程技術而言),就是用與處理伺服器返回的其他文本片段相同的方法來處理它。換言之,基本上就是忽略資料格式,只關注伺服器的響應。 在這種情況下,您要使用請求對象的 responseText 屬性,就像在伺服器向您發送非 XML 響應時一樣(參見 清單 4)。 清單 4. 將 XML 作為普通伺服器響應處理
function updatePage() { if (request.readyState == 4) { if (request.status == 200) { var response = request.responseText; // response has the XML response from the server alert(response); } }} |
|
前文回顧 為避免出現大量重複的代碼,本系列這些後續的文章只給出與所探討的主題相關的那部分代碼。因此,清單 4 僅僅展示了 Ajax 客戶機代碼中的回調方法。如果您對於此方法在更大的非同步應用程式環境中的位置尚不清楚,應回顧本系列的前幾篇文章,那些文章介紹了 Ajax 應用程式的基礎知識。參考資料 中列出了前幾篇文章的連結。 |
|
在這個程式碼片段中,updatePage() 是回調方法,request 是 XMLHttpRequest 對象。最終,您將得到把所有一切串聯在一起的 XML 響應,位於 response 變數之中。如果輸出此變數,您會得到類似於清單 5 的結果。(請注意,清單 5 中的代碼在正常情況下應該是連續的一個程式碼。這裡採用多行形式是為了顯示正常。) 清單 5. response 變數的值
<ratings><show><title>Alias</title><rating>6.5</rating></show><show><title>Lost</title><rating>14.2</rating></show><show><title>Six Degrees</title><rating>9.1</rating></show></ratings> |
這裡,要注意的最重要的地方就是 XML 是作為整體啟動並執行。在大多數情況下,伺服器不會使用空格和斷行符號來格式化 XML,而是將一切串聯在一起,就像您在 清單 5 中看到的那樣。當然,您的應用程式不會過於在意空格,所以這算不上什麼問題,但確實會使代碼閱讀起來較為困難。 在這裡,您可以使用 JavaScript split 函數來拆分此資料,並通過基本的字串操作來獲得元素的名稱和值。毫無疑問,這是個令人頭疼的過程,它無視於您花費了大量時間來閱讀前幾期文章中 DOM(Document Object Model)相關內容這一事實。因此,我要強調,您應該牢記:利用 responseText ,可以便於使用和輸出伺服器的 XML 響應,但我不會為您展示太多的代碼,在能夠使用 DOM 時,您不應選擇這種方法來獲得 XML 資料,下面您會看到這一點。 將 XML 當成 XML 儘管可以 將伺服器的 XML 格式的響應視同為其他任何文本響應來處理,但這樣做沒有很好的理由。首先,如果您一直忠實地閱讀這個系列,那麼您已經學會了如何使用 DOM 這種對 JavaScript 友好的 API 來操縱 XML。還有更好的事情,JavaScript 和 XMLHttpRequest 對象提供了一個屬性,它能完美地擷取伺服器的 XML 響應,並且是以 DOM Document 對象的形式來擷取它。 清單 6 給出了一個執行個體。這段代碼與 清單 4 類似,但沒有使用 responseText 屬性,回呼函數使用的是 responseXML 屬性。該屬性在 XMLHttpRequest 上可用,它以 DOM 文檔的格式返回伺服器的響應。 清單 6. 將 XML 當作 XML
function updatePage() { if (request.readyState == 4) { if (request.status == 200) { var xmlDoc = request.responseXML; // work with xmlDoc using the DOM } }} |
現在您有了一個 DOM Document ,可以像處理其他任何 XML 一樣處理它。例如,隨後可能要擷取所有 show 元素,如 清單 7 所示。 清單 7. 擷取所有 show 元素
function updatePage() { if (request.readyState == 4) { if (request.status == 200) { var xmlDoc = request.responseXML; var showElements = xmlDoc.getElementsByTagName("show"); } }} |
如果您熟悉 DOM,從這兒開始,看起來就應該有幾分熟悉了。您可以使用您所瞭解的全部 DOM 方法,輕鬆操縱從伺服器處接收到的 XML。 當然,您也可以混用普通的 JavaScript 代碼。例如,可以遍曆所有 show 元素,如 清單 8 所示。 清單 8. 遍曆所有 show 元素
function updatePage() { if (request.readyState == 4) { if (request.status == 200) { var xmlDoc = request.responseXML; var showElements = xmlDoc.getElementsByTagName("show"); for (var x=0; x<showElements.length; x++) { // We know that the first child of show is title, and the second is rating var title = showElements[x].childNodes[0].value; var rating = showElements[x].childNodes[1].value; // Now do whatever you want with the show title and ratings } } }} |
通過這段非常簡單的代碼,您正是將 XML 響應作為 XML 而不是無格式的純文字進行了處理,還使用了一點 DOM 和簡單的 JavaScript 來處理伺服器響應。更重要的是,您使用了標準化的格式 —— XML,而不是以逗號分隔的值或以豎線分隔的成對的名稱和數值。換句話說,您將 XML 用在了最適合它的地方,避免了在不適合的地方使用它(比如說向伺服器發送請求時)。 伺服器端的 XML:一個簡單的樣本 我不會談太多有關如何在伺服器上產生 XML 的內容,但花點兒時間看一個簡單的樣本是值得的,我沒有給出過多的注釋,以便您自行思考如何應對這樣的情境。清單 9 展示了一個簡單的 PHP 指令碼,假設有一個非同步客戶機發出了請求,該指令碼將輸出 XML 來應答此請求。 這是一種強力(brute force)的方法,PHP 指令碼實際上是手動產生 XML 輸出。您可以找到許多工具包和 API,用於 PHP 和其他眾多允許您產生 XML 響應的伺服器端語言。無論您的情況如何,這都至少能讓您瞭解產生 XML 並以之應答請求的伺服器端指令碼看上去是什麼樣子。 清單 9. 返回 XML 的 PHP 指令碼
<?php// Connect to a MySQL database$conn = @mysql_connect("mysql.myhost.com", "username", "secret-password");if (!conn) die("Error connecting to database: " . mysql_error());if (!mysql_select_db("television", $conn)) die("Error selecting TV database: " . mysql_error());// Get ratings for all TV shows in database$select = 'SELECT title, rating';$from = ' FROM ratings';$queryResult = @mysql_query($select . $from);if (!$queryResult) die("Error retrieving ratings for TV shows.');// Let the client know we're sending back XMLheader("Content-Type: text/xml");echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>";echo "<ratings>";while ($row = mysql_fetch_array($queryResult)) { $title = $row['title']; $rating = $row['rating']; echo "<show> echo "<title>" . $title . "</title>"; echo "<rating>" . $rating . "</rating>"; echo "</show>";}echo "</ratings>";mysql_close($conn);?> |
您應能夠使用您偏愛的伺服器端語言以類似的方式輸出 XML。IBM developerWorks 上有許多文章可以協助您瞭解如何使用您喜愛的伺服器端語言產生 XML 文檔(相關連結請參見 參考資料)。
解釋 XML 的其他可選方法 除將 XML 作為無格式文本處理或使用 DOM 處理之外,還有一種非常流行的選擇很重要,值得一提。那就是 JSON(JavaScript Object Notation),這是一種綁定在 JavaScript 內的自由文字格式設定。這篇文章中沒有足夠的空間介紹 JSON,在後續文章中將探討相關內容;只要提起 XML 和 Ajax 應用程式,您就很可能會聽到有人談論 JSON,因此現在我將告訴您,您的工作夥伴們在談論的究竟是什麼。 大體上,可以用 JSON 做的事,用 DOM 都可以完成,反之亦然;選擇主要取決於偏好,當然也要為特定的應用程式選擇正確的方法。就現在而言,您應堅持使用 DOM,在接收伺服器響應的過程中熟悉 DOM。我將佔用幾期文章的篇幅、用大量的時間去探討 JSON,之後您就可以隨意選擇在下一個應用程式中使用那種技術了。請繼續關注本系列:後續文章中將介紹關於 XML 的更多內容。
結束語 從本系列上一篇文章開篇起,我幾乎一直在談論 XML,但對於 XML 在 Ajax 應用模式中的貢獻而言,我觸及的依然僅僅是表面。在下一期文章中,您將更詳細地瞭解那些您將希望 發送 XML 的特殊情境(還會看到那些需要接收 XML 的情境)。具體來說,您會在 Ajax 互動的角度上觀察 Web 服務 —— 包括專有 Web 服務和 Google 那樣的 API。 簡而言之,最重要的任務就是思考對於您的應用程式,XML 在什麼時候才是有意義的。在很多情況下,如果您的應用程式工作良好,XML 只不過是又一個讓您頭疼的技術時髦詞彙,您應該克制住僅僅為了宣稱應用程式中有 XML 而嘗試它的衝動。 如果您處於這樣一個環境中:伺服器向您發送的資料非常有限,或者採用的是奇怪的逗號分隔、豎線分隔的格式,那麼 XML 可能會給您帶來真正的收益。考慮處理或更改您的伺服器端組件,以使它們用更為標準化的方式 —— 使用 XML —— 來返迴響應,而不是使用那些健壯性比 XML 差的專用格式。 最重要的是,要認識到,您對 Ajax 相關技術瞭解的越多,就必須越謹慎地制定決策。編寫那些 Web 2.0 應用程式確實很有趣(在後續文章中,您將重返使用者介面,看看可以在這裡做些什麼很酷的東西),但要注意,確保您不是為了向朋友炫耀而在一個可以正常工作的 Web 頁面上濫用這些技術。我確信,您能編寫出優秀的應用程式,那麼就動手去做吧。完成後,再回來閱讀下一期文章,學習關於 XML 的更多知識。 參考資料 學習
- 您可以參閱本文在 developerWorks 全球網站上的 英文原文 。
- 掌握 Ajax 系列:閱讀本系列的其他文章。
- developerWorks 中國網站 XML 專區:訪問 developerWorks XML 專區,獲得大量的技術文章與技巧、教程、標準和 IBM 紅皮書。
- developerWorks 中國網站 Web Development 專區:擷取關於 Web 2.0、Ajax、wikis、PHP、mashup 和其他 Web 項目的資源。
- developerWorks 中國網站 Open source 專區:獲得開放源碼開發與實現的資源。
- “用 JSON 處理緩衝”(Bakul L. Patel,developerWorks,2006 年 11 月):學習在用戶端緩衝驗證中繼資料。
- “技巧:使用 StAX 編寫 XML 文檔”(Berthold Daum,developerWorks,2004 年 1 月):閱讀這篇簡短的技巧性文章,瞭解使用低級、基於遊標的 StAX API 建立 XML 文檔的一種方法。
- “Servlets and XML: Made for each other”(Doug Tidwell,developerWorks,2000 年 5 月):閱讀這篇文章,瞭解如何將 Java servlet 與 XML 一起使用,並從伺服器端產生 XML。
- “XML 問題:使用 Python 模組 xml2sql 和 dtd2sql”(David Mertz,developerWorks,2001 年 6 月):這篇示範性文章介紹了 Python 提供的兩個流行的 XML 相關模組,學習產生 SQL 陳述式以建立並填充一個資料庫。
- “面向 Java 開發人員的 Ajax:構建動態 Java 應用程式”(Philip McCarthy,developerWorks,2006 年 10 月):從 Java 的角度看一看伺服器端的 Ajax。
- “面向 Java 開發人員的 Ajax:Ajax 的 Java 對象序列化”(Philip McCarthy,developerWorks,2005 年 10 月):從 Java 的角度觀察如何通過網路發送對象並與 Ajax 互動。
- “使用 AJAX 調用 SOAP Web 服務,第 1 部分:構建 Web 服務客戶機”(James Snell,developerWorks,2006 年 1 月):深入研究這篇較為進階的文章,它介紹了將 Ajax 與現有基於 SOAP 的 Web 服務相整合的有關內容,說明了如何使用 Ajax 設計模式實現一個基於 網頁瀏覽器的 SOAP Web 服務。
- xml.com:最易於理解的線上資源之一,如果您還不是一名經驗豐富的 XML 程式員,那麼就從這裡開始瞭解關於 XML 的一切吧!
- World Wide Web Consortium 的 DOM 首頁:訪問一切 DOM 相關技術的起點。
- The DOM Level 3 Core Specification:定義核心 Document Object Model,從可用類型和屬性一直到在 DOM 在各種語言中的使用。
- The ECMAScript language bindings for DOM:如果您是一名 JavaScript 程式員,希望在代碼中使用 DOM,那麼可能會對 Level 3 Document Object Model Core 定義的這份附錄感興趣。
- “Ajax: A new approach to Web applications”(Jesse James Garrett,Adaptive Path,2005 年 2 月):閱讀這篇定位 Ajax 的文章 —— 這是所有 Ajax 開發人員的必讀文章。
- developerWorks 技術活動 和 網路廣播:隨時關注這些面向技術開發人員的軟體簡報。
|