rss reader开发资料
1)<<ajax in action>>书中 ch13/refactor/下的内容,
如要增加自己的rss feed,可在RSSReader_refactored.htm 中的
rssFeeds数组添加,如添加本blog的feed: http://www.cnblogs.com/mill2002/rss
则为:
function createRSSReader() {
var options = { rssFeeds: [ "http://www.cnblogs.com/mill2002/rss",
...
"http://radio.javaranch.com/lasse/rss.xml"] };
2)设计原则:见雨哲的
blog
RSS作为Web2.0的一项重要特征和应用,其重要性不必多说,在这里按照个人的认识和想法,简单实现一种RSS的定制方案。
首先,简单的分析一下需求,分析过程忽略,主要的需求和要解决的问题大概有以下几个方面:
1.RSS源提供的RSS显示格式和显示内容统一性的问题
2.Ajax支持以及Ajax支持下的跨域访问的问题
3.RSS源xml文件的编码问题
4.前端显示方式和留给用户操作的易用性问题
5.要记住用户个性化的RSS定制方案
对于问题1,各大RSS 内容提供很好的遵循了所谓的业界规范,如新浪NBA的RSS结构大致如下:
Code
<rss version="2.0">
<channel>
<title>
<![CDATA[篮球-NBA新闻]]>
</title>
<image>
<title>
<![CDATA[体育-篮球]]>
</title>
<link>http://sports.sina.com.cn/basketball</link>
<url>http://www.sinaimg.cn/ty/up/1_6-64-1322-3419_2003080523227.gif</url>
</image>
<description>
<![CDATA[篮球-NBA新闻]]>
</description>
<link>http://sports.sina.com.cn/basketball/index.shtml</link>
<language>zh-cn</language>
<generator>WWW.SINA.COM.CN</generator>
<ttl>5</ttl>
<copyright>
<![CDATA[Copyright 1996 - 2007 SINA Inc. All Rights Reserved]]>
</copyright> <pubDate>Mon, 24 Dec 2007 06:41:43 GMT</pubDate> <category> <![CDATA[]]>
</category>
<item>
<title>
<![CDATA[苏群每周评:绿衫军雄霸天下 火箭雄鹿如患难兄弟]]> </title>
<link>http://sports.sina.com.cn/k/2007-12-24/14353379491.shtml</link>
<author>WWW.SINA.COM.CN</author> <guid>http://sports.sina.com.cn/k/2007-12-24/14353379491.shtml</guid>
<category>
<![CDATA[篮球-NBA新闻]]>
</category>
<pubDate>Mon, 24 Dec 2007 06:35:16 GMT</pubDate>
<comments></comments>
<description>
<![CDATA[ 以下为新浪体育特约专栏作者苏群排出的0708赛季NBA第八周(12月18日-12月24日)各队实力排行榜:
1.凯尔特人 22-3 2
-1 1 -
主场输给活塞队,但周末最后一战对魔术时又以103比91取胜,仍然是东部主场战绩最强队伍。目前凯尔特人保持联盟防守第一,最近12场比赛中,.]]>
</description>
</item>
<item>
结构同上一个item,此处省略
</item>
</channel>
</rss>
可以看出,首先是一些关于RSS内容自身的描述性信息,如语言、链接地址、版权信息等等,然后是若干个以<item></item>并列的真正的内容区域,内容一般包括标题、摘要、发布时间、所属分类、评论等等信息,这样的目录结构在业界遵循RDF(即资源描述)的规范,因此,其他RSS内容提供商所提供的RSS也是这种标准结构的,这使得我们可以放心的订阅任何符合规范的网站RSS内容。但也不是万事大吉了,可以看到,提供的内容太多,而我们想显示给用户的无非就是一个新闻标题,一个日期和一个摘要,特别是一些内容提供网站如搜狐,经常在其摘要中加入图片,如果我们原封不动的让其显示出来,有可能会影响到统一性和美观,想到这里,我们接下来要做的事情就很明确了,就是有选择的在自己的RSS订阅显示区域只显示最重要和最感兴趣的内容,而不是全部内容。这就引出了另外一项需要掌握的技术:xslt,它是专门用来转换承载了RSS内容的xml文档的,有一系列自己语法和特点,这里不细讲,可以参考:xslt手册,它提供了一套较为全面的教程。在我这里,最终需要的xslt文件内容如下:
Code
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"/>
<xsl:template match="/">
<xsl:for-each select="/rss/channel/item [position() <= 5]">
<div class="item">
<a href="{./link}" style="font-size:14px; margin-right:10px; color:#42835B;" target="_blank">
<xsl:value-of select="./title"/>
</a>
<span style="font-size:11px; color:red;">
<xsl:value-of select="substring(./pubDate,6,20)"/>
</span>
<div class="content" id="content" style="line-height:22px; margin:4px 0px 4px 0px; text-indent:24px;">
<xsl:value-of select="./description" disable-output-escaping="yes"/>
<xsl:if test="system-property('xsl:vendor')='Transformiix'">
<script type="text/javascript">
var element = document.getElementById("content");
element.innerHTML = el.textContent;
</script>
</xsl:if>
</div>
</div>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
以上的xslt规定了从源RSS里面选取前5条内容的标题,发布时间和摘要信息,并按照我们自己规定的格式予以显示
还有一个问题是,我们现在虽然控制了要选取和现实的项,但是项的内容我们还没有进行处理,而恰恰其中的图片和一些js脚本是我们不想要的,所以务必过滤一下,一方面减少网络字节传输,另一方面符合了我们想要的内容。过滤主要用到了正则表达式,可以在服务端(C#.net)和客户端(javascript)中进行,如在后台过滤js脚本,可以这样来进行:
string regexstr = @"<script.*</script>";
str = Regex.Replace(str, regexstr, string.Empty, RegexOptions.IgnoreCase);
当然别忘了引用 System.Text.RegularExpressions 命名空间在javascript中过滤图片信息可以这样来进行:
var reg =/<img.*?>/ig;
str=str.replace(reg,"");
在客户端进行的代价是那些代表图片的html代码字节已经传送到了客户端,会稍稍影响下载速度吧,但毕竟<img />标签不像js那样可能会大段大段的,所以这个影响是可以忽略的。
至此,第一个问题的各个方面都有了一个比较好的解决接
下来我们来看第二个问题,ajax支持和ajax跨越的问题,现在主流浏览器都支持ajax,要使用ajax支持,无非是写一些构造ajax过程的js代
码,当然也可以用成熟的js框架,如jquery,重点是第二个问题,为了保证安全性,ajax是不允许跨越的(这个问题百度一下,一堆一堆的),我们要
做的让它可以“跨域”,当然,这里的跨域是打上了引号的,作为引用,这里将采用的不过是一种曲线方案,而让用户觉察不到。
我选取的方案是利用.net强大而完善的功能来先借助服务器下载各个不同域上的RSS内容文件,解析处理后发送至请求的客户端,在这里我们主要用到了.net的HttpWebRequest和HttpWebResponse类,它们位于System.Net命名空间下,利用它们可以请求RSS内容提供商URL上的RSS文件,当然,获取该文件后我们要做一系列的处理,这些处理包括将返回的数据流格式转化成字符串格式,利用前面讲到的xslt来获取我们想要的RSS选项,过滤其中的js代码,最后推送到客户端等等,核心代码如下:
Code
XmlDocument doc = new XmlDocument();
string reqUrl = Request.Params["url"].ToString();//请求的RSS地址
HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(reqUrl);
HttpWebResponse myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
Stream receiveStream = myHttpWebResponse.GetResponseStream();
Encoding encode = System.Text.Encoding.GetEncoding("utf-8");
StreamReader readStream = new StreamReader(receiveStream,encode);
Char[] read = new Char[256];
int count = readStream.Read(read, 0, 256);
String str = String.Empty;
while (count > 0)
{
str += new String(read, 0, count);
count = readStream.Read(read, 0, 256);
}
myHttpWebResponse.Close();
readStream.Close();
string regexstr = @"<script.*</script>";
str = Regex.Replace(str, regexstr, string.Empty, RegexOptions.IgnoreCase);
str = str.Replace(" ", string.Empty);
doc.LoadXml(str);
XslCompiledTransform transformer = new XslCompiledTransform();
transformer.Load(Server.MapPath("xml.xslt"));
System.Text.StringBuilder SB = new System.Text.StringBuilder();
transformer.Transform(doc, null, new System.IO.StringWriter(SB));
Response.Write(SB.ToString());
Response.Flush();
这里主要用到了System.IO、System.Text、System.Xml、System.Xml.Xsl、System.Net和System.Text.RegularExpressions命名空间
至此,跨域请求RSS的问题和在服务器端对RSS进行处理的工作已经完成,在客户端的ajax请求获取从后台发送过去的RSS内容之后,只要在客户端按照预想的格式进行解析和DOM封装,便可以正确的显示RSS内容了。
问题三:RSS源xml文件的编码问题,一般说来标准的RSS文件编码都是utf-8的,显示不会有问题,但个别站点提供的可能是gb2312类型的编码,这类RSS文件容易显示成乱码,对于这个问题,我目前还没有找到比较优雅的办法,在这里先征集一下方案吧,呵呵
问题4.前端显示方式和留给用户操作的易用性问题
前端显示方面,在这里选取了时下比较流行的tab方式,占据页面空间小,切换方便,前面说过可以考虑jquery那样的框架,的确,jquery是一个很好的框架,除了有很多可以简化
开发的功能外,它的第三方插件也是非常丰富的,这里就直接选用了一个基于jquery的tab插件,当
然,都是一些基于jquery的js代码,你完全可以根据自己的需要对其进行改进,以适合特定的需求。该插件的调用方式如下:先引用jquery.js,
然后是tab插件相关的ui.tabs.css、ui.tabs.js,在页面里添加如下html代码
<div id="RssContainer" style=" float:left; 100%">
<ul>
</ul>
</div>
初始化时这样调用:
$('#RssContainer ul').tabs({ fxSlide: true, fxFade: true, fxSpeed: 'normal',event: 'mouseover' });
大致意思是以正常速度和鼠标经过事件处理tab的切换,还另外加上了滑动和消隐效果
接下来是用户操作的问题,也就是要提供一种方式来让用户订阅和管理自己的RSS,在这里用户可以从推荐的列表中选择RSS,也可以自己手动添加互联网上的任意RSS地址,而对于
已经订阅的RSS,也可以实施简单的管理,如取消订阅等。管理区域的html代码如下:
Code
<div id="manageRssArea" class="floatWindow">
<div style="float: right; clear: both; height: 25px; 100%; background: url(images/floatDivbg.gif) no-repeat;">
<div id="manageRssShow">
管理RSS</div>
<div id="addRssShow" class="tabStyle">
添加RSS</div>
</div>
<div id="addRssArea" style="clear: both;">
<p style="margin: 5px 0px 0px 0px;">
<input id="fromChoosed" name="addWay" type="radio" checked="checked" /><span class="rssFont">从推荐中选择</span> <input
id="addByHand" name="addWay" type="radio" /><span class="rssFont">手动添加</span></p>
<div class="floatDivLine">
</div>
<p id="exsitRssList">
<input name="exsitRss" type="checkbox" value="Fun-娱乐~~http://rss.yule.sohu.com/rss/yuletoutiao.xml" />搜狐-娱乐<input
name="exsitRss" value="star-星座~~http://astro.women.sohu.com/rss/xingzuoxinwen.xml"
type="checkbox" />搜狐-星座</p>
<div id="rssItem" style="display: none; margin-top: 20px;">
<p>
<span>名称:</span><input class="textBoxStyle" style=" 100px;" id="rssName" type="text" /></p>
<p>
<span>地址:</span><input class="textBoxStyle" style=" 250px;" id="rssUrl" type="text" /></p>
</div>
<p>
<input id="subBookRss" class="btnStyle rssFont" type="button" value="提交" />
<input class="btnStyle rssFont" onclick="$('#manageRssArea').css('display','none');"
type="button" value="关闭" /></p>
</div>
<div id="manageRss" style="display: none; clear: both;">
<p style="clear: both;">
已经订阅的RSS列表,去掉勾选可以取消订阅</p>
<ul>
</ul>
<p style="clear: both;">
<input id="modifyBook" class="btnStyle rssFont" type="button" value="提交" />
<input class="btnStyle rssFont" onclick="$('#manageRssArea').css('display','none')"
type="button" value="关闭" /></p>
</div>
</div>
相关的js代码在阐述完第五个问题后一起给出
问题五:要记住用户个性化的RSS定制方案,记住有两重含义,在服务端数据库里为每个用户记录其定制方案,或者在客户端的cookie里记录,前者消耗服
务器资源,而且需要用户登录才知道用户是谁,后者嘛,也有问题,客户端禁用缓存或者删除cookie时会失效,至于cookie有效期的问题,我们可以设
置为永不过期。当然客户端的方案还有一个问题就是一个客户端原则上只有一个用户的记录,除非你装了多个不同内核的浏览器。在这里,我选择了客户端的方案,
为此要引入cookie.js
写到这里发现代码实在有些多,因此就不全贴出来了,最后完整的例子在附件中给出。
完整的例子下载