65.9K
CodeProject 正在变化。 阅读更多。
Home

创建 WebDav 到 RSS 的适配器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.48/5 (12投票s)

2006年2月28日

CPOL

4分钟阅读

viewsIcon

50516

downloadIcon

307

如何开发一个读取 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 应返回 trueProcessRequest 包含我们所有的代码。

关于 WebDAV

WebDAVWeb-based Distributed Authoring and Versioning)是对 HTTP 协议的一组扩展,它除了标准的 GET 和 POST 方法外,还提供了额外的“动词”。Outlook Web Access 使用它来提供丰富的 Web 界面。

我们将在本模块中使用的动词是 SEARCH,它提供了一种类似 SQL 的语法来查询文件夹。

关于 XSLT

XSLTeXtensible 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 文件即可支持其他格式。

© . All rights reserved.