開始學習 Atom 激動人心的所有方面。
Atom 聯合
作為一種聯合格式,Atom 來源於各種 RSS(有很多)的實踐而不是完全從頭創建的,只不過明確了 RSS 規范中含糊的地方使其更有用(比如 RSS 沒有規定 title 元素能否包含標記),改正有問題的一些地方(比如確定不同提要中的重復項,這種情況在聚合內容中經常出現)。原來用於 RSS 提要的多數客戶機和工具現在都支持或准備支持 Atom 內容,既然它解決了 RSS 中存在的問題,我推薦使用這種格式。但基本上來說 Atom 是對 RSS 的逐步改進而不是革命性的改造。Atom 采取的一個演化步驟是支持兩種基本類型的聯合文檔:提要和項。提要與 RSS 類似,都是項的集合。但項也可以是獨立的文檔,本身包含一張帖子(可能是一條新聞或者 blog 貼子)或者對外部文檔的引用,比如圖片。優於兩者兼備,所以這種聯合格式為建立發布協議提供一個靈活的層面。
清單 1 顯示了 ATom 提要的一個小例子:
清單 1. Atom 提要
<?xml version='1.0' encoding='UTF-8'?>
<feed XMLns='http://www.w3.org/2005/Atom'>
<id>urn:uuid:E14A6C6B-A832-4AE9-9D67-263181407D5E</id>
<link rel="self" href="/temp/index.atom"/>
<updated>2006-04-19T18:43:55Z</updated>
<title type='text'>Cool gizmos</title>
<subtitle type='text'>The latest in high-tech gizmos.</subtitle>
<author>
<name>Dethe Elza</name>
<email>dethe.elza@livingcode.org</email>
</author>
<entry>
<id>urn:uuid:2C31F522-20A6-44DF-AA63-6524441FF6A3</id>
<published>2006-05-02T19:00:10Z</published>
<updated>2006-04-30T20:57:20Z</updated>
<title type='text'>This new gizmo is hot, hot hot!</title>
<content type='text'>I just got my hands on the latest gizmo
that's sweeping the nation: it's totally rad.</content>
</entry>
</feed>
協議、更多的協議和 API
作為發布協議,Atom 具有更大的抱負。曾經嘗試過幾次創建管理 Web 內容(比如 blog)的通用協議(盡管常常被誤稱為 API),但要麼缺少必要的功能,要麼需要工作區或者私有接口。最重要的是,這些嘗試都沒有利用好的 Web 應用實踐,如 REST。LiveJournal 是第一次嘗試,但是它把一切都通過 HTTP POST 傳輸而忽略了 GET、PUT 和 DELETE,也沒有使用 HTTP 身份驗證。XML-RPC 也走了同樣的路線,與 LiveJournal 協議一樣通過 URI 發送所有信息。直到最近,XML-RPC 還將字符串限制為 ASCII,因而首先就放棄了 XML 的主要優點之一。雖然 XML-RPC 本身不是一個發布協議,但一些協議是以它為基礎的,包括 Manila RPC、Blogger API、MetaWeblog API 以及 LiveJournal XML-RPC ClIEnt/Server 協議。這些協議都不支持國際化,都使用明文傳遞口令,都不容易擴展。Atom Publishing Protocol 充分利用了 XML(國際化,可使用 XML 名稱空間擴展)和 HTTP(所有的方法、身份驗證、用 URI 標識資源)。
Atom Publishing Protocol 以 weblog 協議為基礎但又超越了 weblog 協議,是一種管理 Web 內容的方便工具,得到很多應用,其中包括 Bugzilla、Google Data APIs Protocol 以及 上一期 “XML 問題” 文章中提到的很多日程安排站點,還有一個當時沒有提到但下面就要討論的重要站點。
我認為 Atom Publishing Protocol 是通往可寫入 Web 的征途中的一個重要裡程碑。從來 Web 都是雙向的,PUT 和 DELETE 方法一直是 HTTP 的一部分,但在讀/寫 Web 的前進途中出了岔子。我們已經通過 wiki、XML-RPC 和大塊頭 WebDAV 緩慢地轉回原來的 Web 之路。WebDAV 或簡稱 DAV,即分布式編輯和版本協議(Distributed Authoring and Versioning),其目標是 “完成 Web 的最初目標,成為一種可寫的、協作媒介”(引自 WebDAV FAQ)。因此將 APP 與 DAV 進行對比是公平的。DAV 提供的功能遠遠超過 APP,包括阻塞、任意元數據的存儲以及存儲資源的刪除或重命名。公平地說,Atom 也能支持任意元數據,因為很容易通過 XML 名稱空間擴展。Atom 實際上對協作的支持比較弱,只有發布(包括以後的編輯),因此對阻塞的需要不大,而且可以用 DELETE 後跟 PUT 來對資源重命名。Atom 完成這些功能不需要擴展 HTTP 或者增加新的方法。WebDAV 極其復雜,一直受到主供應商糟糕的、漏洞百出的實現的困擾。即便如此,WebDAV 社區仍然不滿足 DAV 對 HTTP 的擴展,因此又層層加碼(或者准備)作進一步的擴展,比如 Advanced Collections、Versioning and Configuration Management(因為您知道,Distributed Authoring and Versioning 不支持版本化)和 Access Control。但是這些擴展對日程安排來說還不夠,因此出現了 CalDAV,它對 HTTP 做了更多擴展。
可能毫不奇怪,WebDAV 一直未能成功地征服 Web。難以實現,而且設置和管理都很麻煩。DAV 也有自己的成功支持,特別是 Subversion,這個版本控制系統被誇耀成 CVS 的後繼者。Subversion 建立在 DAV 和 Versioning 擴展(也稱為 DeltaV)的基礎上,雖然它僅僅選擇 DAV 中相關的部分而丟掉了其他功能,然後在這個混合物中加上自己的協議。雖然 Subversion 很成功,但是它實際上沒有證明 DAV 巴洛克式的復雜性的正確性。對於 DAV 功能中的 90%,我認為 APP 更合適,剩下的 10% 可放到其他系統中。APP 本身並不是一個包羅萬象的終極解決方案。它本身沒有解決身份驗證的問題,沒有提供查詢機制,當然也沒有支持實時協作這類功能的打算。我認為讀/寫式 Web 仍在成長之中,我期望也許有一天 Jabber XMPP 協議會嵌入到 Web 浏覽器中作為雙向實時端對端協議,但這是後話了。
James Tauber 正在從事一個與 Atom 和 Subversion 有關的 Python 項目,稱為 Demokritos,該項目提供了 Atom Store。有趣的地方(至少對於本文來說)在於底層使用 Subversion 來提供持久性。項目仍然處在早期階段(今後的版本將增加身份驗證),但是值得關注其進展。Google Base 可以看作是 Atom Store 的商業版本(雖然多數上傳使用現在已經過時的 Atom Syndication Format 0.3 版),Amazon 的 S3 數據存儲從使用 HTTP GET/PUT/DELETE 這一點看在概念上類似於 Atom Publishing Protocol。有選擇當然好,但是如果所有的選擇都統一到一個簡單、健壯的標准上就更好了。
Atom Publishing Protocol 的工作原理是,客戶機可以查詢自省(introspection)文檔,這類文檔列出了提供的內容集合、能力(比如可讀和可寫)及其地址 URI。然後客戶機可以查詢集合本身來發現與其自身包含的內容類似的信息,可以是 Atom EntrIEs 或圖片、音頻、視頻之類的媒體。集合是 Atom 提要,增加新的材料只需要 PUT 一個 Atom Entry 文檔並收到指向該資源的 URI,然後可以對該資源作進一步處理(用 POST 編輯,用 GET 讀,用 DELETE 刪除)。比如,清單 2 是一個簡單的自省文檔:
清單 2. 自省文檔的例子<?XML version="1.0" encoding='utf-8'?>
<service XMLns="http://purl.org/atom/app#">
<workspace title="Gizmo Page" >
<collection
title="Cool gizmos"
href="http://example.org/gizmo/index.atom" >
<member-type>entry</member-type>
</collection>
<collection
title="Photos of Gizmos"
href="http://example.org/gizmo/image" >
<member-type>media</member-type>
</collection>
</workspace>
</service>
這個自舉過程中仍然有一個問題:一開始如何發現自省文檔?有一個雖然過期但是廣泛實現的 IETF 草案 Atom Feed Autodiscovery,它描述了如何在 HTML 頁面的元數據中嵌入一個或多個 Atom 提要引用。該方法對自省文檔也同樣有效。這項技術很簡單,在 Html 文檔的 <head> 中插入一個 <link> 元素,其 rel 屬性包含關鍵字 “alternate”,type 屬性值為 “application/atom+xml”,href 屬性指向一個 Atom 提要。雖然 Atom 工作組沒有明確說明這種方法如何用於自省文檔,但大致與上述形式類似,只不過如 Atom wiki 上所述作以下變更:rel 屬性為 “introspection”,type 屬性必須是 “application/atomserv+XML”,href 則指向一個自省文檔而不是提要。無論哪種情況,<link> 元素都應該在 title 屬性中包含人類可讀的值。比如:
<link rel="instrospection" type="application/atomserv+XML"
href="/introspection.atomsrv" title="All about my feeds"/>
為什麼 Atom 必須支持微格式?
上一期 “XML 問題” 文章中,我提到只有與 Atom 結合起來微格式才能真正得到應用。這句話現在仍然有效。Uche Ogbuji 是一位 Atom 擁趸,但是對微格式的使用持不同意見。專門針對 Atom 的微格式目前有兩種(據我所知):hAtom 是 Atom 的一個子集,此外還有關於 “XHtml Microformats for the Atom Publishing Protocol” 的一個 IETF 草案,它提出了兩種微格式用於在 APP 中描述類別和錯誤。我還沒有發現這些微格式的應用。
最有趣的是,微格式是用於嵌入其他文檔的,而 Atom 文檔的目的是包含 HTML 或 XHtml 片段(以小心控制的方式)。因此沒有理由 Atom 提要中不能嵌入 hCalendar 制定的日歷。事實上,就在上一篇文章進入最終編輯階段時,Google 發布了它的 Calendar 產品,該產品允許以 Atom 格式訂閱日歷提要。不幸的是,雖然能夠以包含 iCalendar 信息的方式獲得提要,但 Google 沒有在提要中提供 hCalendar。一些人注意到了這一點,編寫了 GreaseMonkey 腳本之類的東西在網頁中發現 hCalendar 並將其增加到 Google Calendar 中,但我希望選擇另一條路,使用我的 Google Calendar Atom 提要向 hCalendar 格式的 weblog 增加事件。快速搜索後沒有發現別人做過這個,我只好自己寫了一個簡單、粗糙的腳本。我是用 Python 編寫的,它需要兩個第三方庫:httplib2 和 ElementTree。這僅僅是個例子,與一般的例子一樣沒有包含錯誤檢查,也不是很靈活。清單 3 顯示了示范其用法的測試函數:
清單 3. Atom 到 weblog 的腳本'''
Utility to grab a Google Calendar feed and return hCalendar code
This module has one public function:
events_for_feed(feed_uri, start, end) -> [hCalendarEvents]
'''
import StringIO
import httplib2
import cElementTree
_ATOM_NS = 'http://www.w3.org/2005/Atom'
_GDATA_NS = 'http://schemas.google.com/g/2005'
_MONTHS = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
_EVENT_TEMPLATE = '''<div class="vevent"
XMLns="http://www.w3.org/1999/xHtml">
<abbr class="dtstart" title="%(start)s">%(start_hr)s</abbr> -
<abbr class="dtend" title="%(end)s">%(end_hr)s</abbr> -
<span class="summary">%(summary)s</span> - at
<span class="location">%(where)s</span>
<div class="description">%(description)s</div>
</div>
'''
def _end_human_readable(ts):
return ts[11:16]
def _start_human_readable(ts):
month = _MONTHS[int(ts[5:7], 10)]
return '%s %s, %s - %s' % (month, ts[8:10], ts[:4], ts[11:16])
def _entrIEs_for_feed(feed_uri, start, end):
h = httplib2.Http('.cache')
resp, content = h.request('%s?start-min=%s&start-max=%s' %
(feed_uri, start, end), 'GET')
doc = cElementTree.parse(StringIO.StringIO(content))
return doc.findall('//{%s}entry' % _ATOM_NS)
def _event_for_entry(entry):
when = entry.find('{%s}when' % _GDATA_NS)
start = when.get('startTime')
start_hr = _start_human_readable(start)
end = when.get('endTime')
end_hr = _end_human_readable(end)
where = entry.find('{%s}where' % _GDATA_NS).get('valueString')
summary = entry.findtext('{%s}title' % _ATOM_NS)
description = entry.findtext('{%s}content' % _ATOM_NS)
return locals().copy()
def events_for_feed(feed_uri, start, end):
return [_EVENT_TEMPLATE % _event_for_entry(entry) for entry in
_entrIEs_for_feed(feed_uri, start, end)]
def test():
feed_uri = 'http://www.google.com/calendar/feeds/\
dethe.elza@gmail.com/public/full'
start = '2006-04-30T00:00:00'
end = '2006-05-30T00:00:00'
for event in events_for_feed(feed_uri, start, end):
print event
if __name__ == '__main__':
test()
將該腳本增加到 Calendar 提要中並提供起始日期和結束日期,就會返回一列 hCalendar 格式的字符串,可以插入到 weblog 中。這就是讀/寫式 Web 的優美之處。如果某一方不支持您的格式,但是他們使用的格式是開放的並提供相應文檔,就像 google 那樣,您可以根據需要自行創建。我的觀點是,微格式和 Atom 是互不可分的,這一觀點雖然還沒有得到證實,但至少以不同的方式說明了兩者可以協同工作(仍然有很多工作要做)。
結束語
關於 Atom Publication Protocol 的研究仍在繼續,其他相關規范如 Google Calendar 擴展同樣如此。網站在快速地采用 Atom,應用程序和編程工具也在適應 Atom。開放的格式、可擴展性和清晰的定義,使得 Atom 對於 Web 影響力有可能像關系數據庫對企業一樣。HTTP GET 和 VIEw Source 時至今日仍然是一種有效的組合,就像在 Web 早期一樣。
通過這篇簡短的介紹,我希望您能了解 Atom Syndication 格式的重要性以及 Atom Publication Protocol 如何簡化它的使用。