创建 WebDav 到 RSS 的适配器






4.48/5 (12投票s)
如何开发一个读取 Exchange WebDAV 文件夹并返回 RSS 的处理程序。
引言
RSS 是供稿的标准格式。每种平台都有数百种阅读器和聚合器。而另一方面,WebDAV 是一个晦涩难懂、文档记录不全/支持不好的协议。
本文的目的是创建一个适配器,允许用户使用 RSS 阅读器访问通过 WebDAV 暴露的 Microsoft Exchange 文件夹。
我们将使用 IHttpHandler
、XSLT、WebDAV 和 HTTP 身份验证,所有这些都在背景部分进行了介绍。
Using the Code
设置 WebDAVAdapter 非常简单
- 在 IIS 中创建一个空应用程序
- 配置它以使用 ASP.NET 2.0 和匿名访问
- 将源文件复制到虚拟目录
- 将您的 RSS 阅读器指向http://WEBSERVER/VDIR/GetFeed.ashx?URL=http(s)://EXCHANGESERVER/Exchange/USER/FOLDER/
背景
关于 IHttpHandler
HTTP 处理程序已经存在很多年了。那些.asp“页面”本质上就是这样:处理程序类接收一个 HTTP 请求,没有任何继承结构地进行处理,并输出文本和标头。
ASP.NET 的.aspx页面是更复杂的类,它们继承了 IHttpHandler
并为处理模型添加了抽象,以便更容易开发事件驱动的网页。
IHttpHandler
仅提供两个方法
public interface IHttpHandler
{
bool IsReusable { get; }
void ProcessRequest(HttpContext context);
}
如果另一个请求可以使用 IHttpHandler
实例,则 IsReusable
应返回 true
。ProcessRequest
包含我们所有的代码。
关于 WebDAV
WebDAV(Web-based Distributed Authoring and Versioning)是对 HTTP 协议的一组扩展,它除了标准的 GET 和 POST 方法外,还提供了额外的“动词”。Outlook Web Access 使用它来提供丰富的 Web 界面。
我们将在本模块中使用的动词是 SEARCH,它提供了一种类似 SQL 的语法来查询文件夹。
关于 XSLT
XSLT(eXtensible Stylesheet Language Transformation)是一种用于将 XML 文档转换为其他 XML 文档的语言。它基于**模板**,这些模板匹配源文档的节点并生成目标中的标记。
关于 HTTP 身份验证
HTTP 身份验证在RFC 2617 中进行了描述。它描述了一种服务器要求客户端提供一套凭据的方法。
要使用**基本**方案进行身份验证,客户端必须发送一个包含以下内容的 Authorization 标头:
Authorization: Basic user:pass
其中 *user:pass* 以 Base64 编码。
如果标头不存在,或者凭据无效,则会返回 401 (Unauthorized) 状态码,并添加一个如下所示的 Authenticate 标头:
WWW-Authenticate: Basic realm="The Site"
当浏览器找到此标头时,它通常会向用户显示一个对话框,要求输入用户名和密码,然后重试请求。
代码
我们需要做的第一件事是创建一个 WebDAV 文件夹 URL 的请求
string url = context.Request.QueryString["URL"];
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.Method = "SEARCH";
request.ContentType = "text/xml";
请注意,Method
(动词)已从默认的 GET 更改为 SEARCH。我们还设置了 ContentType
,以便服务器知道我们在请求正文中发送 XML。
接下来,我们解析 Authentication 标头并为请求创建一个凭据
string authorizationHeader = context.Request.Headers["Authorization"];
if (!string.IsNullOrEmpty(authorizationHeader))
{
//Format is "Basic BASE64STRING"
string userPassBase64 = authorizationHeader.Split(' ')[1];
byte[] userPassBytes = Convert.FromBase64String(userPassBase64);
string userPassString = Encoding.Default.GetString(userPassBytes);
//Format is user:pass
string[] userPassArray = userPassString.Split(':');
request.Credentials = new NetworkCredential(userPassArray[0], userPassArray[1]);
}
我们需要对请求流的引用
using (Stream requestStream = request.GetRequestStream())
{
using (XmlTextWriter writer = new XmlTextWriter(requestStream, Encoding.UTF8))
然后,我们使用它来写入 SEARCH 查询
writer.WriteStartDocument();
writer.WriteStartElement("searchrequest", "DAV:");
writer.WriteStartElement("sql", "DAV:");
writer.WriteString(string.Format(@"
SELECT
""urn:schemas:httpmail:subject"",
""urn:schemas:httpmail:fromname"",
""urn:schemas:httpmail:date"",
""urn:schemas:httpmail:htmldescription""
FROM SCOPE('Deep traversal of ""{0}""')
WHERE ""DAV:ishidden"" = False
", url));
writer.WriteEndDocument();
SCOPE('Deep traversal of "url"')
告诉服务器也要在子文件夹中查找消息。您可以在此处找到 Exchange Store 属性的参考,并在此处找到 SEARCH 方法的描述。
现在,最重要的一部分。
我们从服务器获取响应流,并对其设置一个 XmlReader
using (WebResponse response = request.GetResponse())
{
using (Stream responseStream = response.GetResponseStream())
{
using (XmlTextReader reader = new XmlTextReader(responseStream))
我们加载转换并创建一个表示文件夹链接的参数
XslCompiledTransform transform = new XslCompiledTransform();
transform.Load(context.Request.MapPath("RSS.xsl"));
XsltArgumentList arguments = new XsltArgumentList();
arguments.AddParam("link", string.Empty, url);
最后,我们将转换后的 XML 发送到客户端
context.Response.ContentType = "text/xml";
transform.Transform(reader, arguments, context.Response.OutputStream);
如果提供的凭据不正确,授权可能会失败。我们将通过将 GetResponse()
包装在 try-catch
块中并再次请求客户端身份验证来处理这种情况
catch (WebException ex)
{
if ((ex.Response as HttpWebResponse).StatusCode == HttpStatusCode.Unauthorized)
{
context.Response.StatusCode = 401;
context.Response.AppendHeader("WWW-Authenticate",
string.Format(@"Basic realm=""{0}""", url));
}
else
{
//rethrow on other errors
throw;
}
}
如果到目前为止没有问题,请求将完成,您的 RSS 阅读器将显示文件夹中每条消息的一个条目。
转换
我们使用的 XSLT 非常简单。
首先,声明我们正在使用的样式表和命名空间
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:d="urn:schemas:httpmail:" xmlns:a="DAV:">
为链接 URL 添加一个参数。参数很有用,尤其是在包含不是输入文档一部分的数据时。
<xsl:param name="link"/>
接下来是创建 rss
元素的“根”模板。apply-templates
元素将选择每个项(消息)并将其传递给相应的模板。
<xsl:template match="/">
<rss version="2.0">
<channel>
<title>
<xsl:value-of select="$link" />
</title>
<link>
<xsl:value-of select="$link" />
</link>
<xsl:apply-templates select="a:multistatus/a:response" />
</channel>
</rss>
</xsl:template>
匹配的模板创建 RSS item
元素及其 link
,并再次使用 apply-templates
处理其余属性
<xsl:template match="a:response">
<item>
<link>
<xsl:value-of select="a:href/text()" />
</link>
<xsl:apply-templates select="a:propstat/a:prop" />
</item>
</xsl:template>
最后一个模板完成 item
属性
<xsl:template match="a:prop">
<title>
<xsl:value-of select="d:subject/text()" />
</title>
<author>
<xsl:value-of select="d:fromname/text()" />
</author>
<date>
<xsl:value-of select="d:date/text()" />
</date>
<description>
<xsl:value-of select="d:htmldescription/text()" />
</description>
</xsl:template>
进一步开发(家庭作业!)
虽然此示例仅支持 RSS,但添加例如Atom 支持非常简单。您需要:
- 创建一个新的Atom.xsl文件
- 参数化
XslCompiledTransform
的加载(例如,通过QueryString
发送所需的格式)
一旦转换加载被参数化,您只需要添加新的 XSL 文件即可支持其他格式。