<rss version="2.0">
...
</rss>
<rss version="2.0">
<channel>
<title>Latest DataWebControls.com FAQs</title>
<link>http://datawebcontrols.com</link>
<description>
This is the syndication feed for the FAQs
at DataWebControls.com
</description>
<item>
<title>W(wǎng)orking with the DataGrid</title>
<link>http://datawebcontrols.com/faqs/DataGrid.Aspx</link>
<pubDate>Mon, 07 Jul 2003 21:00:00 GMT</pubDate>
</item>
<item>
<title>W(wǎng)orking with the Repeater</title>
<description>
This article examines how to work with the Repeater
control.
</description>
<link>http://datawebcontrols.com/faqs/Repeater.Aspx</link>
<pubDate>Tue 08 Jul 2003 12:00:00 GMT</pubDate>
</item>
</channel>
</rss>
SELECT TOP 5 ArticleID,Title,Author,Description,DatePublished FROM Articles ORDER BY DatePublished DESC
<%@ Page language="AutoEventWireup="false" Inherits="SyndicationDemo.rss" %>
<Asp:Repeater id="rptRSS" runat="server">
<HeaderTemplate>
<rss version="2.0">
<channel>
<title>Asp.NET News!</title>
<link>http://www.AspNETNews.com/Headlines/</link>
<description>
This is the syndication feed for AspNETNews.com.
</description>
</HeaderTemplate>
<ItemTemplate>
<item>
<title><%# FormatForXML(DataBinder.Eval(Container.DataItem,
"Title")) %></title>
<description>
<%# FormatForXML(DataBinder.Eval(Container.DataItem,
"Description")) %>
</description>
<link>
http://www.AspNETNews.com/Story.Aspx?ID=<%#
DataBinder.Eval(Container.DataItem, "ArticleID") %>
</link>
<author><%# FormatForXML(DataBinder.Eval(Container.DataItem,
"Author")) %></author>
<pubDate>
<%# String.Format("{0:R}",
DataBinder.Eval(Container.DataItem,
"DatePublished")) %>
</pubDate>
</item>
</ItemTemplate>
<FooterTemplate>
</channel>
</rss>
</FooterTemplate>
</Asp:Repeater>
在我們生成在線新聞聚合器之前,讓我談談這個聚合引擎一些可能的增強功能。首先,每一次訪問 rss.Aspx 頁面的時候,都要訪問一次數(shù)據(jù)庫。如果預期可能有大量的人頻繁地訪問 rss.Aspx 頁面,使用輸出緩存是很有價值的。其次,通常新聞網(wǎng)站會將聚合的內(nèi)容分為不同的類別。例如:News.com 有一些專門的聚合內(nèi)容區(qū), 比如針對企業(yè)計算、電子商務、通信的內(nèi)容等等。在數(shù)據(jù)庫表 Articles 中加入表示類別的 Category 字段就可以很容易地提供這種支持。這樣 一來,在 rss.Aspx 頁面中,可以接收一個表示顯示分類的查詢參數(shù),然后只搜索指定的新聞項分類即可。
[dvnews_page=用Asp.NET建立一個在線RSS新聞聚合器]
在 Asp.NET 頁面中使用聚合摘要
為了測試我們剛建立的聚合引擎,我們將創(chuàng)建一個在線新聞聚合器,允許采集任意數(shù)量的聚合內(nèi)容摘要。聚合器的界面很簡單,參見圖二。它包括三個框架頁面。左邊框架以列表形式列出了不同的聚合內(nèi)容摘要。右上部框架顯示所選的聚合內(nèi)容摘要包含的新聞項以及查看該新聞項的鏈接。最后,在右下部框架則顯示選中的新聞項標題和內(nèi)容。順便提及一下,這樣的界面基本上是各種類型的聚合器的一個事實上的標準界面,包括新聞聚合器、email客戶端軟件和新聞組閱讀器都是這樣的界面。
圖二 新聞聚合器用戶界面的截圖
第一步是創(chuàng)建一個html頁面來建立框架用戶界面。幸運的是,在Visual Studio.NET 2003 中,這一過程非常容易。只需要在Web應用程序解決方案中添加一個新 的項目,選擇新項目類型為 Frameset。(我在我的工程中將這個新文件命名為 NewsAggregator.htm。我之所以將它設置為 html 文件而不是 Asp.net 頁面, 是因為這個頁面只包括建立框架的 html 代碼。每一個單獨的框架會顯示一個 Asp.net 頁面)。下一步,參見圖三,F(xiàn)rameset 模版向?qū)樱唵蔚剡x擇選項“Nested Hierarchy”,然后按ok按鈕就可以了。
圖三 VS2003 中 Frameset 模版向?qū)М嬅?
然后 Frameset 模版向?qū)?chuàng)建一個HTML頁面,里面已經(jīng)加入了框架的源代碼。 只要將左邊框架的src屬性設置為 DisplayFeeds.Aspx,它是列表顯示聚合摘要 Asp.net 頁面的 url。至此 NewsAggreator.htm 頁面就完成了。
以下三個部分,我們將講述如何創(chuàng)建在線新聞聚合器的三個組件,它們分別是顯示聚合摘要列表的 DisplayFeeds.Aspx;顯示特定聚合摘要新聞項 的 DisplayNewsItems.Aspx;以及顯示指定聚合摘要特定新聞項具體內(nèi)容的 DisplayItem.Aspx。
顯示聚合摘要列表
現(xiàn)在我們需要創(chuàng)建 DisplayFeeds.Aspx 頁面。該頁面要顯示訂閱的聚合摘要列表。作為示范,我將這些聚合摘要放在一個叫 Feeds 的數(shù)據(jù)庫表中。當然你也可以將它們放在一個XML文件中。表 Feeds 有如下四個字段:
·FeedID—主鍵,自增長整數(shù)類型,唯一標示一個摘要
·Title—摘要名稱,數(shù)據(jù)庫字段類型:varchar(50)
·URL—RSS 摘要的 URL,數(shù)據(jù)庫字段類型:varchar(150)
·UpdateInterval—摘要更新頻率(分鐘),數(shù)據(jù)庫字段類型:int
DisplayFeeds.Aspx 頁面使用一個 DataGrid 控件顯示聚合摘要的列表。這個 DataGrid 只有一個 HyPerlinkColumn 列,顯示 Title 字段的內(nèi)容并且鏈接到 DisplayNewsItems.Aspx 頁面, 在查詢字符串中 要傳遞 FeedID 字段的值。以下是 DataGrid 控件的聲明,為簡單起見,省略了一些無關的部分): <Asp:DataGrid id="dgFeeds" runat="server"
AutoGenerateColumns="False" ...>
...
<Columns>
<Asp:HyPerlinkColumn Target="rtop"
DataNavigateUrlField="FeedID"
DataNavigateUrlFormatString="DisplayNewsItems.DataTextField="Title" HeaderText="RSS Feeds">
</Asp:HyPerlinkColumn>
</Columns>
</Asp:DataGrid>
這里要注意的關鍵是 HyPerlinkColumn 列的定義。它的 Target 屬性設置為右上部分框架的名稱,這樣當用戶點擊的時候,DisplayNewsItems.Aspx 頁面就會顯示在右上部分的框架中。另外, 屬性 DataNavigateUrlField、DataNavigateUrlFormatString 和 DataTextField 也做了相應的設置, 以便超鏈接顯示摘要的標題,并且當點擊它時,就會將用戶帶到 DisplayNewsItems.Aspx 頁面,并在查詢串中將 FeedID 字段的內(nèi)容傳 過來。(該頁面的后臺代碼類只訪問來自 Feeds 表的摘要清單,按照 Title 字段的字母順序返回,接著將查詢結(jié)果綁定到 DataGrid 控件。 由于篇幅所限,本文在此不列出代碼。)
[dvnews_page=用Asp.NET建立一個在線RSS新聞聚合器]
顯示特定聚合摘要的新聞項<script language="// display a blank page in the bottom frame when the news items loads
parent.rbottom.location.href = "about:blank";
</script>
<?XML version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0"
<xsl:output method="html" omit-
<xsl:template match="/rss/channel">
<b><xsl:value-of select="title"
disable-output-escaping="yes" /></b>
<xsl:for-each select="item">
<li>
<a>
<xsl:attribute name="href">
DisplayItem.Aspx?ID=<xsl:number value="position()" />
</xsl:attribute>
<xsl:attribute name="target">rbottom</xsl:attribute>
<xsl:value-of select="title"
disable-output-escaping="yes" />
</a>
(<xsl:value-of select="pubDate" />)
</li>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
<a href="DisplayItem.
之所以要使用這種語法,是因為要給 XSLT 樣式表中某個你要創(chuàng)建的元素添加一個屬性,然后在該元素的標簽里使用 <xsl:attribute> 語法 。有關該語法的一些例子可在 W3Schools 網(wǎng)站上找到:The <xsl:attribute> Element。
最后要注意的是,超鏈接的ID查詢字符串的值是來自于 <xsl:number> 元素,從 position() 函數(shù)中返回的值。<xsl:number> 元素僅僅是輸出一個數(shù)值。position()函數(shù)是一個 XPath 函數(shù),用來返回 XML 文檔中當前節(jié)點的順序位置。這意味著對于第一個新聞項,position() 函數(shù)返回 1,第二個 新聞項,position函數(shù)返回 2,以此類推。我們需要記錄這個值并將它通過查詢字符串傳遞出去。這樣當 DisplayItem.Asp 頁面被訪問時,就可以知道顯示 RSS 聚合摘要的什么項目了。
聰明的讀者可能已經(jīng)注意到,我們的 XSLT 樣式表沒有全部完成,因為 FeedID 參數(shù)沒有通過查詢字符串傳遞到 DisplayItem.Aspx 頁面。要明白 這是為什么,我們回顧一下在 ID 查詢串參數(shù)中所傳遞的是用戶擬察看詳細信息的<item>元素順序號。也就是說,如果用戶點擊第四條新聞項,頁面 DisplayItem.Aspx?ID=4 就會被 加載到右下部分的框架中。問題在于 DisplayItem.Aspx 頁面無法確定用戶希望查看哪一個摘要。有兩個不同的方法可以解決這個問題,比如可以在右下部框架中用客戶端 JAVAscript 代碼讀取右上部框架的 URL,然后確定FeedID 的值。在我看來,更簡單的辦法是和 ID 參數(shù)一起將 FeedID 的值通過查詢字符串傳遞 。
這樣的話,有一個難題是 XSLT 樣式表操縱的 RSS XML 數(shù)據(jù)中并沒有 FeedID 值。但是 DisplayNewsItems.Aspx 頁面知道 FeedID 值,需要一種方法讓 XSLT 樣式表也知道這個值。通過使用 XSLT參數(shù)可以 實現(xiàn)完成。
XSLT 參數(shù)的使用是非常簡單。在 XSLT 樣式表中,你需要在 <xsl:template> 元素中加入一個<xsl:param> 元素, 該元素提供參數(shù)的名稱。下面的代碼將這個參數(shù)命名為 FeedID:
<xsl:stylesheet version="1.0"
<xsl:template match="/rss/channel">
<xsl:param name="FeedID" />
...
</xsl:template>
</xsl:stylesheet>
現(xiàn)在,就可以用下面的語法在<xsl:value-of>元素中使用這個參數(shù)了:
<xsl:value-of select="$parameterName" />
最后,在我們的 XSLT 樣式表中加入下面的代碼,我們就可以把 FeedID 查詢字符串參數(shù)加到超鏈接中了:
<a>
<xsl:attribute name="href">DisplayItem.Aspx?ID=<xsl:number value="position()" />&FeedID=<xsl:value-of select="$FeedID"
/></xsl:attribute>
注意在ID查詢字符串參數(shù)后面我們加了一個&字符(轉(zhuǎn)義&),這樣我們就可以傳遞 FeedID 參數(shù)的值到查詢字符串的 FeedID 參數(shù)中了。 這就是我們要在 XSLT 樣式表中添加的內(nèi)容。
剩下的工作是在 DisplayNewsItems.Aspx 頁面的 Page_Load 事件處理函數(shù)中設置這個參數(shù)的值。通過使用 XsltArgumentList 類可以完成這一工作。這個類有一個 AddParameter() 方法。一旦我們創(chuàng)建了這個類的一個實例,加入了相應的參數(shù),就可以將這個 實例賦給 XML Web 控件的 TransformArgumentList 參數(shù)了。下面的代碼顯示了更新后的 DisplayNewsItems.Aspx 頁面 Page_Load 事件處理函數(shù):
顯示特定新聞項的詳細內(nèi)容
還剩下最后一件需要做的事情是顯示用戶選擇的特定新聞項的詳細內(nèi)容。這些詳細內(nèi)容將顯示在右下部的框架中,而且將會顯示新聞項的標題,描述和新聞項的鏈接等信息。和 DisplayNewsItem.Aspx 頁面 類似,DisplayItem.Aspx 頁面首先將根據(jù)傳入的 FeedID 查詢字符串參數(shù)獲取遠程的 RSS 聚合摘要,然后它會用 XML Web 控件顯示這些詳細內(nèi)容。實際上,DisplayItem.Aspx 頁面的 Page_Load 事件處理函數(shù)和DisplayNewsItem.Aspx 頁面的 該函數(shù)幾乎一樣,只有以下兩個小小的區(qū)別:
·DisplayItem.Aspx 頁面需要讀取ID查詢字符串參數(shù)的值;
·DisplayItem.Aspx 頁面使用一個 XSLT 參數(shù),但是這個參數(shù)與 DisplayNewsItem.Aspx 頁面用的參數(shù)是不一樣的;
DisplayNewsItem.Aspx 和 DisplayItem.Aspx 頁面一樣都需要在參數(shù)中傳遞一個 XSLT 樣式表。DisplayNewsItem.Aspx 頁面?zhèn)鬟f的是 參數(shù) FeedID,而 DisplayItem.Aspx 還需要傳入 ID 參數(shù),它表示 XSLT 樣式表應該顯示那個新聞項。這個細小的差別在以下代碼中以粗體顯示,以下 代碼省略了與 DisplayNewsItems.Aspx 頁面相同的部分:
以下是轉(zhuǎn)換 XML 數(shù)據(jù)的 XSLT 樣式表:
<?XML version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0"
<xsl:output method="html" omit-<xsl:param name="ID" />
<xsl:template match="/rss/channel">
<b><xsl:value-of select="item[$ID]/title"
disable-output-escaping="yes" /></b>
<p>
<xsl:value-of select="item[$ID]/description"
disable-output-escaping="yes" />
</p>
<a>
<xsl:attribute name="href"><xsl:value-of
select="item[$ID]/link" /></xsl:attribute>
<xsl:attribute name="target">_blank</xsl:attribute>
Read More...
</a>
</xsl:template>
</xsl:stylesheet>
注意 <xsl:param> 元素被用于聲明 ID XSLT 參數(shù)。然后,在幾個不同的 <xsl:value-of> 元素中,ID 參數(shù) 被用來從 <item> 元素列表中抓取特定的 <item> 元素。在 XPath 的語法中,elementName[i]意思是根據(jù)相應元素名 存取第i個元素。例如,item[1]將只獲取第一個<item>元素,item[2]則獲取第二個元素。所以 item[$ID]是獲取由 XSLT 參數(shù) ID 定義的 特定 <item> 元素。
最后,值得注意的還有在樣式表靠近末尾部分的超鏈接 Read More…,它的target屬性設為空,這樣的話當用戶點擊 Read More… 鏈接的時候,瀏覽器會打開一個新的窗口。
[dvnews_page=用Asp.NET建立一個在線RSS新聞聚合器]
未來的擴展和當前程序的缺點
本文講述的代碼中有一個明顯的缺點就是每次用戶點擊左邊框架的某個聚合摘要或者在右上部框架點擊某個新聞項時,遠程聚合摘要都會被裝載和解析。每次用戶點擊遠程聚合 摘要時,所有的項都被加載,這樣的效率無疑是很差的。每次用戶點擊一個新聞項標題就重新裝載整個遠程聚合摘要也是很浪費資源的。這樣的方法不僅沒有效率,對提供發(fā)布服務的個人或者公司也是不禮貌的,因為這些 連續(xù)的、不沒必要的請求占用了他們的 Web 服務器的負載資源。
這個缺點在本文附帶的源代碼中已經(jīng)得到解決。具體來說,.NET數(shù)據(jù)緩存可以用來存放不同摘要的 XMLDocument 對象。緩存間隔設置為數(shù)據(jù)表 Feeds 中 UpdateInterval 字段定義的值。(當然,由于某些原因,摘要的 XMLDocument 對象有可能會被提前清除出緩存)
這個系統(tǒng)的另外一個缺點是在右上部框架和右下部框架之間沒有狀態(tài)的保存。為了說明這樣會引起什么問題,考慮以下的動作:
·用戶點擊左邊框架的某個聚合摘要鏈接,在右上部框架中裝載這個摘要的新聞項目。假設這個摘要的UpdateInterval 的值是30,則表示這些內(nèi)容在30分鐘之 后會過期;
·裝載右上部框架的新聞項的同時,這些內(nèi)容被緩存起來;
·用戶離開去吃午飯;
·發(fā)布聚合內(nèi)容的網(wǎng)站增加了一條新的新聞項;
·我們的用戶一個小時午飯后回來了,這個 摘要的 XMLDocument 的緩存已經(jīng)過期;
·用戶點擊右上部框架的第一條新聞項,將會在右下部分框架中裝載 DisplayItem.Aspx,傳入 ID 參數(shù)值1;
·DisplayItem.Aspx 頁面在緩存中沒找到 XMLDocument 對象,只好重新獲取遠程摘要。這樣就會獲得新的數(shù)據(jù)了(別忘了,步驟 4 已經(jīng)加了一個新的新聞項),然后此頁面會顯示第一條新聞項目(因為ID參數(shù)的值為1) ;
·用戶看到了新的新聞項,但是內(nèi)容會令他感到有點困惑,因為已經(jīng)不是他所點擊的那一條新聞了,而且右上部也沒有顯示那條新的新聞。
之所以出現(xiàn)這樣的問題,是因為 ID 參數(shù)沒有唯一地標識一個新聞項,它只是一個特定時間點上新聞項列表中的一個偏移量。解決這個問題的一個好的方法是不要用數(shù)據(jù)緩存來保存聚合 摘要,而是使用數(shù)據(jù)庫或者持久介質(zhì)的其它方式(比如 Web 服務器本地文件系統(tǒng)的 XML 文件)。如果使用數(shù)據(jù)庫,每一個新聞項都可以擁有一個唯一的標識號,可以用來傳遞到右下角的框架中。這種方法可以保證解決上面提到的問題。當然也會增加系統(tǒng)的復雜性,比如需要決定何時從數(shù)據(jù)庫中清除掉舊的新聞項 。
本文現(xiàn)有的應用程序還缺少異常處理,而這肯定是應該加上的。尤其是當從遠程 RSS 聚合摘要文件獲取數(shù)據(jù)并加載到 XMLDocument 對象時,應該加上異常處理。因為遠程的文件可能不存在或者格式不正確。
還有很多增強功能可以輕松地加入到這個在線新聞聚合器。一個明顯的功能是需要一個管理頁面來允許用戶添加,刪除和編輯他們現(xiàn)在的聚合摘要。還有,如果能允許用戶自定義分類 ,將他們的聚合摘要按類別放在一起就更好了。另外,現(xiàn)在的用戶界面還是比較粗糙的,但是通過增加一些 XSLT 樣式表生成的 HTML 代碼或者在幾個框架里面增加一些樣式表就可以很容易地美化一下界面。最后,在html標簽里面加一些<meta>元素,可以讓右上部框架定時地去刷新,使得用戶不用自己手工去刷新頁面就可以看到最新的新聞項目。
注解 (2003年8月4日): 在這篇文章發(fā)布以后,一些讀者用 Email 告訴通知我在顯示特定 RSS 聚合項的 <description> 元素時,有兩個潛在的問題:
1、Disable-output-encoding 屬性,這個屬性用在 <xsl:value-of> 元素中,但是并不是所有的 XSLT解析器都實現(xiàn)了這個功能。.NET XSLT 解析器支持 disable-output-encoding,但是還是要 注意一下,因為讀者可能試圖將這個應用程序移植到其它平臺。
2、<description> 元素的 HTML 內(nèi)容是被原封不動地輸出的。但是,這些 HTML 內(nèi)容可能包含惡意代碼,比如 <script> 或者 <embed> 代碼塊。理想情況下,這些代碼應該被剔除掉。為了清除掉這些有潛在危險的代碼,可能需要用到一些擴展函數(shù)(參見 Extending XSLT with JScript, C#, and Visual Basic .NET)。想查看從 RSS 聚合 摘要剔除 HTML 內(nèi)容的更多信息,可以參見’’Dive Into Mark’’ 日志.
總結(jié)
在本文中,我們不僅講到如何創(chuàng)建一個聚合引擎,還創(chuàng)建了一個在線新聞聚合器。在建立這兩個應用程序時,我們都采用了在 Asp.NET 頁面顯示 XML 數(shù)據(jù)的技術。在聚合引擎里面,我們使用了 Repeater 控件以 XML格式來顯示數(shù)據(jù)庫中的數(shù)據(jù)。而在新聞聚合器里面,我們使用了 XML Web 控件和 XSLT 樣式表。
我邀請你下載本文的在線新聞聚合器,然后根據(jù)你的需要來增強它。如果有任何關于這個應用程序或者這篇文章討論的概念方面的問題,隨時恭候你的 EMail。我的 EMail mitchell@4guysfromrolla.com.