原文地址:http://msdn.microsoft.com/zh-cn/magazine/cc135976.aspx
作者:
Justin Smith 是 Microsoft 一名专门从事 Web 服务的技术专家。他致力于研究 WCF 和 BizTalk 服务。Justin 也是“Inside Windows Communication Foundation”(Windows Communication Foundation 内幕)(Microsoft Press, 2007) 一书的作者,并经常在软件开发会议上发表演讲。您可以在 blogs.msdn.com/justinjsmith 阅读他的博客。
本文以 Visual Studio 2008 的预发布版为基础。文中包含的所有信息均有可能变更。
本文讨论:
| 本文使用了以下技术: Windows Communication Foundation |
目录
当它作为 Microsoft
® .NET Framework 3.0 的一部分首次发布时,Windows
® Communication Foundation (WCF) 包含一个 Plain Old XML (POX) 的通用对象模型,以及可以进行多种传输的 SOAP 消息。由于 WCF 也可以深入支持 WS-* Web 服务标准,因此它可以毫不费力地与其他现代服务平台进行互操作。.NET Framework 3.0 中的 WCF 具有高度可扩展性,而且拥有可靠的可管理性功能,如消息记录、活动跟踪、服务限制、实例管理以及线程控制。
.NET Framework 3.5 中的 WCF 构建于 .NET Framework 3.0 的扩展点之上,从而为构建符合 Web 原则的服务提供一流的支持。它包含一个易于使用的 HTTP 编程模型、JavaScript Object Notation (JSON) 消息传递功能,以及新的整合 API,使创建和使用整合的内容变得非常简便。凭借此功能集,WCF 现已成为将服务连接到 Web 客户端的首选服务平台,不论这些客户端是 ASP.NET AJAX 控件、Silverlight
TM 客户端,还是浏览器。这些功能在部分信任方案(如 ASP.NET 中等信任)中也可发挥作用,因此您可以在各种宿主环境中承载 WCF 服务。除此之外,Visual Studio
® 2008 中还集成了新的工具,极大地缩短了启动和运行服务所需的时间。
使 .NET Framework 3.5 中的 WCF 如此引人注目的特色之一是,以 Web 为中心的通信与 SOAP 和 WS-* 标准组合到了一个服务堆栈和对象模型中。这意味着您可以构建这样一个服务,即采用 SOAP 和 WS-* 在企业内部或跨企业边界进行通信,同时还可以将同一服务配置为使用 Web 协议与外部通信。实际上,WCF 处理了服务中的烦琐细节工作,这样您可以更加专注于服务所提供的功能。
本文介绍了 .NET Framework 3.5 中 WCF 的一些以 Web 为中心的新功能。首先是关于 HTTP 和 Web 中的一些重要体系结构原则的定级性讨论,然后将为您介绍 WCF 中新的 HTTP 编程模型,最后是新的整合 API。
HTTP 消息基础知识
您可能几乎每天都在使用 Web,但有多少次会考虑使 Web 运转起来的核心概念是什么呢?我并不想重复关于体系结构概念的内容,但结合到 Web 中的某些关键概念并不是那么一目了然。在我看来,要构建符合 Web 协议的服务,对这些原则有个很好的了解是第一步,也是十分必要的。请记住,由于此主题的研究可长达一整本书,因此在本文中我将仅提及一些要点。
HTTP 是 Web 的传输机制。HTTP 要求一种请求/响应消息交换模式 (MEP)。您每次使用浏览器导航到 Web 页时都会发生下面这个过程:您发出一个获取资源的请求,然后一个包含该资源的响应将返回。HTTP 传输会定义若干动词(也称为方法),将功能添加到请求/响应 MEP 上,如 GET、POST、PUT 和 DELETE。为了清楚阐明 HTTP 动词之间的某些差别,我们来看看 HTTP POST 是什么样的。下面是一个简略的版本:
POST /myservice/PostAlbum HTTP 1.1 HOST: www.cloudsample.net <albumInfo> <albumId>15</albumId> </albumInfo>
这实际上只是一个发送给侦听应用程序的消息。在此例中,消息是发送到 http://www.cloudsamples.net/myservice/PostAlbum。消息还有一个负载,包含的是 albumId 字段的值。概括地说,您可以将 HTTP POST 想象成是将负载发送到侦听应用程序的一种方法。
本例的负载十分简单,我可以将整个负载表示成数字 15。要使其工作,接收应用程序需要知道数字 15 是映射到 albumId 字段的值。这是 HTTP GET 中提供的关键功能之一。比方说,这就是从先前的 HTTP POST 转换到 HTTP GET 的结果:
GET /myservice/GetAlbum/15 HTTP 1.1 HOST: www.cloudsamples.net
HTTP GET 是经过优化的 HTTP POST,它无需负载。实际上,URI 就是负载。结果就是可以缩减消息的大小,从而缩短传输时间。缩减的消息以及对 URI 的依赖性还可带来其他的正面效应:它可以方便其他应用程序(如代理程序或浏览器)缓存先前针对同一资源的 HTTP GET 消息的结果。值得注意的是,对 HTTP GET 的使用比任何其他 HTTP 动词都要频繁得多。
HTTP GET 的核心是请求获取某个资源。为了阐明这一点,请想象您在网页(如 http://www.cloudsamples.net/pictureservices)上找到了一个有趣的资源,并且希望用电子邮件将其发送给某位朋友。当您的朋友收到该电子邮件时,您希望他能够单击链接来并看到与您所见相同的页面。如果 http://www.cloudsamples.net/pictureservices 上的资源突然变成了来自 MSNBC 的最新新闻报道,这当然会让您感到惊讶。实际上,HTTP GET 中所使用的 URI 代表的就是一个资源,通常您认为该资源不会改变。
虽然 HTTP GET 代表的内容已有明确定论,但对于其他 HTTP 动词而言并非如此。让我们来考虑一下,将一个 HTTP DELETE 消息发送给某个接收应用程序意味着什么。接收应用程序是否需要更改其状态或执行某个动作呢?通常,HTTP DELETE 是与删减性的状态变化或动作相关联,但该状态变化的具体信息则未定义。HTTP PUT 同样如此。通常,HTTP PUT 是与某些增加性状态变化或动作相关联,但变化的具体情况则因应用程序而异。
表达其他信息
收到 HTTP GET 的应用程序会使用嵌入在 URI 中的信息来确定发送哪个资源作为回复。为了对此进行阐明,让我们来看看下面这组 URI:
contoso.com/artists/Flaming+Hammer/HitMe contoso.com/artists/Northwind/Overdone
在这个例子中,Contoso Corporation 有一个应用程序,提供与音乐相关的资源。从这些例子中您应该可以推断出艺术家姓名以及唱片名称是如何映射到 URI 的:
contoso.com/artists/[artist]/[album]
当应用程序收到 http://contoso.com/artists/Northwind/Overdone 的 HTTP GET 消息后,它会返回一个与 Northwind 的 Overdone 唱片相关的资源。这个 URI 显然有一定的模式。它由一些基本信息 (contoso.com/artists) 和一些 URI 段(或“孔”)组成,它们将包含艺术家和唱片的值。
在查询字符串参数中嵌入同类信息也很普遍。虽然格式与前面的例子不同,但最终结果相同。下面是一组 URI 孔以及一个查询字符串参数:
contoso.com/artists/Flaming+Hammer?album=HitMe contoso.com/artists/Northwind?album=Overdone
在本例中,URI 和查询字符串都使用如下的语法:
contoso.com/artists/[artist]?album=[album]
HTTP 传输还会使用一组可扩展的标头,以表达其他信息。这个信息可以与缓存、消息中数据的类型、发送应用程序的名称、内容的长度、主机操作系统以及其他很多信息相关。
图 1 显示了一条 HTTP GET 消息(1-8 行)以及带有若干常用标头的回复消息(9-14 行)。
Figure 1 HTTP GET Request and Response
1 GET /PictureServices/Feed.svc/picture/Pictures;Bridge.jpg HTTP/1.1 2 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-ms-application, application/vnd.ms-xpsdocument, application/xaml+xml, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, application/x-silverlight, */* 3 Accept-Language: en-us 4 UA-CPU: x86 5 Accept-Encoding: gzip, deflate 6 User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 1.1.4322; .NET CLR 3.5.20706; .NET CLR 3.0.590; MS-RTC LM 8) 7 Host: www.cloudsamples.net 8 Proxy-Connection: Keep-Alive 9 HTTP/1.1 200 OK 10 Content-Type: image/jpeg 11 Server: Microsoft-IIS/7.0 12 X-Powered-By: ASP.NET 13 Date: Sat, 15 Sep 2007 18:57:11 GMT 14 Content-Length: 106333
HTTP GET 请求中的“Accept”标头指出了客户端要接收的数据格式。正如您从那一长串值中所见,客户端可以接收多种类型的数据(图像、Office 文档、Silverlight 应用程序等)。HTTP GET 响应所含数据的格式用 Content-Type 标头的值(如第 10 行的 image/jpeg 所示)来描述。这种简单的机制可以让发送和接收应用程序协调数据格式。用标头来协调数据格式在表达能力上不及 SOAP Web 服务的 Web 服务描述语言 (WSDL) 和 XML 架构定义 (XSD) 语法,但对于 Web 来说已经足够了。
REST 和 Web
通过查看 HTTP 传输的一些关键部分,您也已经看到了将 Web 松散地联结在一起的一些重要原则。2000 年,一位名叫 Roy Fielding 的博士生完成了一篇论文,其中一项工作就是将这些原则汇编形成了一种体系结构风格,也就是所谓的具像状态传输 (Representational State Transfer, REST)。(您可以在
ics.uci.edu/~fielding/pubs/dissertation/top.htm 找到 Roy 的论文。)
各种书籍、软件会议以及看起来无数的博客已经对 REST 体系结构风格进行了详细的讨论。此处不可能对它面面俱到,但我认为应该可以把它浓缩为三个要点:
- 利用 URI
- HTTP GET 具有特殊性
- Content-Type 是数据模型
为了撰写本文的目的,我们假设这些要点就是 Web 的原则。通过将这些原则结合到应用程序中,即可大大增强互操作性以及应用程序功能的作用范围。互操作性和作用范围是大多数 Web 服务举措背后的关键驱动因素。毕竟,Web 服务实质上就是一个通过结构化消息传递架构来提供某种功能的方法。Web(HTTP 请求/响应)的原则实际描述了可能的消息传递架构的子集。事实上,所有平台、应用程序、操作系统和用户都了解 Web 原则,因此将其结合在您的 Web 服务应用程序中即可提高它的作用范围和互操作性。
需要注意的是,由于 REST 和 WS-* 之间的选择并不相互排斥,因此 Web 原则对 WS-* 没有影响。它们中任何一个都不能解决所有类型的通信要求,因此建议您不要在自己构建的所有服务上一股脑地应用其中一个。例如,REST 不擅长于向中介发送消息,而 WS-* 却擅长于此。另一方面,WS-* 应用程序不太擅长与诸如浏览器等简单的客户端交互,但 REST 在这方面却十分出色。
使用 WCF 进行 HTTP 编程
基于这些 Web 基础知识,我们再看看在 .NET Framework 3.5 中使用 WCF 来构建符合 Web 原则的应用程序。首先,我将介绍 HTTP 编程模型中一些新的构建块,然后向上介绍整个系统。
如果要“利用 URI”,您可能需要习惯构建和分析 URI。手动执行这项任务往往容易出错且十分枯燥。然而,新的 System.UriTemplate 和 System.UriTemplateMatch 类型挑起了构建和分析 URI 这个重担,而它们也是 WCF 新的 HTTP 功能中的基础构建块。
UriTemplate 类型是定义上文中所述 URI 孔的基础构建块。UriTemplate 类型的构造函数可以接受代表 URI 中孔的 String。在实例化时,UriTemplate 对象会提供两个实例方法,将文本值与在实例化中定义的孔绑定。这些方法会返回一个 URI,它的孔已填充了值。
UriTemplate 对象同样也会提供一个名为 Match 的实例方法,用于从 System.Uri 对象的孔提取值。Match 实例方法可以接受两个 System.Uri 对象。第一个 Uri 是基本地址,第二个 Uri 是用于匹配的候选项目。System.UriTemplateMatch 对象是 Match 方法返回的值。UriTemplateMatch 对象包含一组值,用于 UriTemplate 实例化时定义的孔。
图 2 中的代码阐明了如何使用 UriTemplate 和 UriTemplateMatch 类型来回处理了一次 Uri。此代码的结果输出如下所示:
Figure 2 Creating a Template-Based URI
// create a URI bound to the template Uri baseAddress = new Uri(@"http://localhost:2000"); UriTemplate template = new UriTemplate("{artist}?album={album}"); Uri boundUri = template.BindByPosition(baseAddress, "Northwind", "Overdone"); Console.WriteLine(boundUri.ToString()); // retrieve the value of the artist segment UriTemplateMatch match = template.Match(baseAddress, boundUri); String bandName = match.BoundVariables["artist"]; Console.WriteLine("the name of the artist is {0}", bandName);
http://localhost:2000/Northwind?album=Overdone the name of the artist is Northwind
System.UriTemplate 和 System.UriTemplateMatch 类型在命名空间中忽略了常见的 WCF System.ServiceModel 标记。这是因为它们的功能使您无需在 WCF 应用程序内部使用这两种类型。任何时候您需要创建或分析 URI,它们使用起来都非常方便。
虽然 UriTemplate 和 UriTemplateMatch 类型使用起来已经非常容易了,但 WCF 小组还想进一步简化这两种类型在 WCF 服务中的使用。目标是提供一种简单而直观的方法,将 URI 段和查询字符串参数映射到应用程序功能中。我想下面代码中所展示的模型就符合这个目标:
[ServiceContract] public interface IPictureService { [OperationContract] [WebGet(UriTemplate = "picture/{pictureId}")] Stream GetPicture(String pictureId); [OperationContract] [WebGet(UriTemplate = "picture/t/{pictureId}")] Stream GetPictureThumbnail(String pictureId); }
UriTemplate 表面上看起来是 WebGetAttribute 上的一个实例属性,而 WebGetAttribute 又随之应用到某个服务约定。请注意,大括号间的标记映射到方法参数的名称。虽然这个示例只显示了一个参数,但您可以添加多个参数,以任何顺序排列,将其作为查询字符串参数,甚至可以使用通配符。
运行时,UriTemplate 属性的值会被传递到 UriTemplate 构造函数。无论是 Client 运行时,还是 Dispatcher,都以这种方式使用 UriTemplate 属性值。Dispatcher 用它将传入消息与某个操作匹配,而 Client 则用它确保代理应用程序上的方法被发送到正确的 URI。这种方法不仅简单易用,而且可以有效地在您的服务中“利用 URI”。
WCF 约定中的 HTTP 动词
在 .NET Framework 3.5 中使用 WCF 进行 HTTP 编程可以轻而易举地将服务约定中的操作映射到 HTTP 动词。顾名思义,将 WebGetAttribute 应用到某个操作可以使该操作能够通过 HTTP GET 使用。正如您在前面的代码片段中所见,WebGetAttribute 定义了一个名为 UriTemplate 的实例属性。
WebGetAttribute 类型还有一些其他实例属性。最引人注目的是 RequestFormat 和 ResponseFormat。如其名称所指,这些属性的值指明了操作的消息格式。RequestFormat 和 ResponseFormat 属性都属于 WebMessageFormat 类型,即一种具有两个值的枚举类型:Xml 和 Json。WCF 基础结构会使用这些属性的值,借用合适的消息编码器来设置通道堆栈。WebMessageFormat.Xml 的属性值将产生使用 XML 编码器的通道堆栈。将值设置为 WebMessageFormat.Json 将产生使用 .NET Framework 3.5 中的 JSON 编码器的通道堆栈。由于 HTTP 消息交换的双方都有各自的属性,因此一个应用程序可以实现接收 XML 消息并返回 JSON 消息(反之亦然)。
另一个与 HTTP 动词相关的属性是 WebInvokeAttribute。将此属性应用到操作会使该操作可以被除 HTTP GET 以外的其他任何 HTTP 动词所使用。对象模型中 HTTP GET 和所有其他动词之间的这种区别反映了 HTTP GET 相对于其他 HTTP 动词的使用频率。WebInvokeAttribute 对象模型与 WebGetAttribute 类似,但它包含一个名为 Method 的实例属性。此属性的值可以将某个特殊 HTTP 动词与操作相关联。Method 属性具有 String 类型,因此您可以将值设置为任何标准的 HTTP 动词,甚至是非标准的 HTTP 动词。
图 3 阐明了如何在服务约定中使用 WebInvokeAttribute。
Figure 3 Using WebInvokeAttribute
[ServiceContract] public interface IPictureService { [OperationContract] [WebGet(UriTemplate = "picture/{pictureId}")] Stream GetPicture(String pictureId); [OperationContract] [WebGet(UriTemplate = "picture/t/{pictureId}")] Stream GetPictureThumbnail(String pictureId); [OperationContract] [WebInvoke(UriTemplate="update", Method="POST")] void UpdatePictureInfo(PictureInfo info); }
UpdatePictureInfo 方法拥有一个 PictureInfo 类型的参数。在运行时,PictureInfo 对象的序列化版本是 HTTP POST 消息的负载。通过这种方式传递数据,可以让应用程序传输 URI 中无法表达的复杂数据类型。
.NET Framework 3.5 中的 WCF 还可以使与 HTTP 标头交互变得很简便。请求和响应 HTTP 标头是通过 System.ServiceModel.Web.WebOperationContext 类型提供的。WebOperationContext 类型是对 System.ServiceModel.OperationContext 类型(在 .NET Framework 3.0 的 WCF 中引入)的扩展,并且它的使用模式相似。两者都适用于服务对象实现的内部。WebOperationContext 类型提供了一些成员,它们简化了读取或设置 HTTP 标头值,并且简化了检索与访问服务对象所用的 URI 相关的信息。HTTP 标头以集合的形式存储,最常用的标头则作为单独的属性提供。下例表明如何通过为 HTTP 响应设置 Content-Type 标头,使用 WebOperationContext 类型沿用 Web 的数据模型:
public Stream GetPicture(string pictureId) { // retrieve the Stream (omitted) Stream stream; // set the Content-Type to image/jpeg WebOperationContext.Current.OutgoingResponse.ContentType = "image/jpeg"; return stream; }
绑定和承载
至此,您已了解如何将 URI 段映射到应用程序功能、如何将操作映射到 HTTP 动词,以及如何与 HTTP 标头交互。现在,让我们来看看如何将这些功能整合到一个运行的服务中去。为此,您需要先了解 WCF 中更多的新类型。
.NET Framework 3.0 中的 WCF 引入了绑定这个概念,即对传输和终结点协议的一种抽象。.NET Framework 3.0 附带了多种绑定,支持各种传输和协议选择。
.NET Framework 3.5 中的 WCF 包含一种新的绑定,名为 System.ServiceModel.WebHttpBinding。这种绑定是对 Web 原则的抽象,它所遵循的使用模型与 .NET Framework 3.0 中的绑定类型相同,因此可以像其他绑定一样将其添加到终结点。
.NET Framework 3.0 中的 WCF 还引入了终结点行为这个概念。行为是一种扩展消息传递基础结构执行路径的方式。.NET Framework 附带了多种行为,要自己创建行为也十分容易。
.NET Framework 3.5 中的 WCF 包含了一种新的终结点行为,名为 System.ServiceModel.Description.WebHttpBehavior。此行为可以执行多种任务。对于接收应用程序而言,它的任务之一是设置筛选基础结构,以确保将收到的消息分派到服务对象上相应的方法。在 .NET Framework 3.0 中,WCF 使用 SOAP Action 和目标地址的组合作为调度键。WebHttpBehavior 设置的筛选机制是对现有筛选机制的一种扩展,目的是使用 UriTemplate 匹配和 HTTP 动词。
图 4 显示了如何使用 WebHttpBinding 和 WebHttpBehavior 来构建利用 Web 原则的接收应用程序。这种方法的优点是,您可以将这些终结点之一添加到现有的 ServiceHost 中,这样您最终将得到一个 ServiceHost,它不仅有 SOAP/WS-* 终结点,同时也有 REST 终结点。
Figure 4 Using WebHttpBinding and WebHttpBehavior
ServiceHost host = new ServiceHost(typeof(IPictureContract), new Uri("http://localhost:5000")); // instantiate a WebHttpBinding WebHttpBinding binding = new WebHttpBinding(); // add an endpoint, using the WebHttpBinding ServiceEndpoint ep = host.AddServiceEndpoint( typeof(IPictureContract), binding, String.Empty); // add the WebHttpBehavior to the endpoint ep.Behaviors.Add(new WebHttpBehavior()); // open the ServiceHost to start listening host.Open();
发送应用程序遵循相同的使用模型,但应将 WebHttpBehavior 添加到 ChannelFactory<T> 对象上的终结点行为集合中。根据典型的 WCF 惯例,也可以在配置文件中使用这些选项。
.NET Framework 3.5 中的 WCF 使得这个模型更加容易使用,在某些情况下,您无需添加 WebHttpBinding 或 WebHttpBehavior。新的 API 包含了两种类型以实现此目的:System.ServiceModel.Web.WebServiceHost 和 System.ServiceModel.Web.WebServiceHostFactory。WebServiceHost 类型可以自动将 WebHttpBehavior 添加到所有终结点,并在运行时执行一些额外的验证,以确保所有的终结点都与该行为兼容。这就省去了将 WebHttpBehavior 添加到终结点这个步骤。
WebServiceHostFactory 类型适用于 IIS 承载的情形,这样就不需要命令性或配置文件承载信息。.NET Framework 3.0 中的 WCF 引入了 .svc 文件,作为在 IIS 中承载 WCF 服务的激活目标。这些文件包含了一些指令,类似于 ASP.NET 中的页面指令。将 WebServiceHostFactory 添加到 .svc 文件指令的 Factory 属性会创建一个 ServiceHost、添加一个终结点(与 WebHttpBinding 使用适当的约定)、将 WebHttpBehavior 添加到该终结点,然后打开 ServiceHost:
<%@ ServiceHost Language="C#" Service="PictureService" Factory="System.ServiceModel.Web.WebServiceHostFactory" %>
服务的 web.config 文件不包含任何与 WCF 相关的条目。
新的整合 API
请想象一个 RSS 源。如果您和我一样,就会立即想到新闻源或博客。整合(RSS 和 ATOM 是整合格式)能够表达的不止是这些。简单地说,整合的内容就是表达一组数据的方式。这组数据实际上可以是任何内容:悉尼方圆 50 英里范围内居住的袋熊的数量、最近 10 张超过 100 美元的采购单,或是 Contoso 上个月生产的回飞棒的数量。
.NET Framework 3.5 充分支持整合内容的创建和使用。它支持 RSS 2.0 和 ATOM 1.0 格式的创建及使用、支持添加整合扩展的各种方式,甚至还支持实现其他格式的功能。编程模型避免了开发人员直接处理整合格式细节,从而使其易于使用。
在 3.5 版之前,.NET Framework 一直没有一种标准的方式来创建或使用整合的内容。虽然新的整合功能由 WCF 小组编写,但它并不依赖于 WCF。事实上,所有与整合相关的类型都是 System.Syndication 命名空间的一部分,虽然它们位于 System.ServiceModel.Web.dll 程序集中。您可以从任何承载了 AppDomain(ASP.NET、WPF 应用程序、NT 服务等)的进程使用 .NET Framework 3.5 的整合功能。这个功能意味着您可以通过任何传输(而不仅仅是 HTTP)提供或使用整合的内容。然而,当与 WCF 中的 HTTP 编程模型结合后,也可以将整合添加到现有的 SOAP/WS-* 服务中。
新的整合 API 包含了一些类型,它们可以抽象单独的整合源及其中的项目,还有一些类型可以将该源转换为特定的格式。System.Syndication.SyndicationFeed 类型是整合源的一种中立格式的表现形式。SyndicationFeed 包含一系列 SyndicationItem 对象。不含一组 SyndicationItem 对象的 SyndicationFeed 就像是一个没有豌豆的豆荚,因为 SyndicationItem 对象就是源中项目的表现形式。
用一组 SyndicationItem 对象填充 SyndicationFeed 后,SyndicationFeedFormatter<T> 可以将该源转换成特定的格式。SyndicationFeedFormatter<T> 可派生出两种类型:Rss20FeedFormatter 和 Atom10FeedFormatter。顾名思义,这些类型可以将 SyndicationFeed 的实例分别转换为 RSS 2.0 和 ATOM 1.0 格式。
创建 SyndicationFeed
创建 SyndicationFeed 对象的方法有两种。您可以实例化对象并手动填充其成员,或者也可以从现有的源来填充整个 SyndicationFeed。这两种方法都非常简单,同时还可以避免开发人员直接处理特定传输格式的琐碎细节。下面的代码演示了如何手动构建 SyndicationFeed 对象。
SyndicationFeed feed = new SyndicationFeed(); feed.Title.Text = "The Cybertopian Chronicle";
Title 仅仅是众多可以设置的属性之一,因此请查看文档以便了解其他可以设置的属性。
很多情况下,您想要读取一个现有的源,并根据该源的内容做一些事情。在新的整合 API 中,您可以实例化一个 SyndicationFeed,并自动从现有的源对其状态进行填充。这时,您只需要一个现有源的 URI,或是一个准备好读取源的 XmlReader。下面的代码显示了如何连接到 Web 上的一个现有源,并从中提取信息:
Uri feedUri = new Uri("http://blogs.msdn.com/justinjsmith/atom.xml"); SyndicationFeed feed = SyndicationFeed.Load(feedUri); Console.WriteLine(feed.Title.Text); // outputs "The Cybertopian Chronicle"
SyndicationItem 类型包含了超过 35 个成员。许多这些成员都是与设置或检索字段(如项目标识符、最后更新时间、发表日期、标题或实际内容)相关的属性。还有许多成员则是为了方便扩展 SyndicationItem 中存储的内容。对 RSS 和 ATOM 的扩展(Microsoft 简单列表扩展、Yahoo Media RSS、GeoRSS 等)有很多,SyndicationFeed 和 SyndicationItem 均可扩展为包含任何现有的 RSS 或 ATOM 扩展。
源可以有许多项目,对于大型源来说,不能一次加载所有项目。SyndicationFeed 解决了这个情况,它将其 SyndicationItem 对象集公开为一个 IEnumerable<SyndicationItem>。这种实现使处理大量 SyndicationItem 对象更为方便,因为它充分利用了 .NET Framework 2.0 的迭代器功能。用 LINQ 也可以遍历一组 SyndicationItem 对象。这就大大减少了从源提取信息所需的代码量。
整合 API 定义了多个类型,它们可以将 SyndicationFeed 转换为 RSS 2.0 和 ATOM 1.0 格式。事实上,您可以构建 SyndicationFeed,用 SyndicationItem 对象填充它,然后将该源公开为 RSS 和 ATOM。
图 5 演示了如何检索一个 ATOM 1.0 源、将其转换为 RSS 2.0,然后将新的 RSS 表示输出到控制台。
Figure 5 Transforming a Feed
// read an ATOM feed Uri feedUri = new Uri("http://blogs.msdn.com/justinjsmith/atom.xml"); SyndicationFeed feed = SyndicationFeed.Load(feedUri); // transform it to RSS Rss20FeedFormatter formatter = new Rss20FeedFormatter(feed); XmlWriter writer = XmlWriter.Create(Console.Out, null); // write it to the Console formatter.WriteTo(writer); writer.Flush();
在 WCF 中将整合 API 与 HTTP 编程模型配合使用时,就可以公开来自自定义 URI 的源,并根据 URI 的构成返回 RSS 或者 ATOM。
图 6 显示了如何在服务约定中定义一个操作,该操作将使用已收到的 HTTP GET 中的一个查询字符串参数来返回 RSS 或 ATOM 源。请注意操作约定中 SyndicationFeedFormatter<SyndicationFeed> 的使用。Rss20FeedFormatter 和 Atom10FeedFormatter 都派生于 SyndicationFeedFormatter<TSyndicationFeed>。
Figure 6 Exposing a Feed from a Customized URI
[ServiceKnownType(typeof(Atom10FeedFormatter))] [ServiceKnownType(typeof(Rss20FeedFormatter))] [ServiceContract] interface IPictureService { [OperationContract] [WebGet(UriTemplate="Pictures?format={format}")] SyndicationFeedFormatter<SyndicationFeed> Feed(String format); } class PictureService : IPictureService { public SyndicationFeedFormatter<SyndicationFeed> Feed(String format){ // create the syndication feed SyndicationFeed feed = new SyndicationFeed(); // add the items to the feed (omitted) // check the argument & return the right format if(format.ToLower() == "rss"){ return new Rss20FeedFormatter(feed); } return new Atom10FeedFormatter(feed); } }
总结
总的来说,您已了解了 HTTP 传输的一些基础知识、它如何与 Web 原则相关联、如何使用 WCF 将这些原则结合到您的应用程序中,以及如何使用新的整合 API 来提供和使用 RSS 以及 ATOM 源。为了进一步演示这些新功能,您可以在一个名为 PictureServices 的示例应用程序(网址为 www.cloudsamples.net/pictureservices)中看到所有功能一起运行。您可以在线运行这个示例、浏览其源代码并下载整个示例。
图 7 显示了运行中的 PictureServices。
Figure 7
The PictureServices Feed (单击该图像获得较大视图)
简言之,PictureServices 可以从本地计算机或 Flickr 源检索图片。该应用程序使用一个简单的提供程序模型,目前有三个提供程序:Windows 桌面搜索、文件系统上的文件夹,以及 Flickr。一旦 PictureServices 从某个提供程序检索到文件,它便会整合这些图片,并且在本地提供它们。您可以在浏览器中单击一个源,然后检索各个图片。示例中还有许多内容,请尽情探索吧。有关本文所述技术的详细信息链接,请参阅“其他资源”侧栏。