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

RESTful WCF - 第二部分

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2009年4月9日

CPOL

5分钟阅读

viewsIcon

50101

RESTful WCF

很久以前(记不清具体是何时了),我曾说过我要写一些关于 RESTFul WCF 的内容,您可以参考我的第 1 篇博文 http://sachabarber.net/?p=460。在那篇文章中,我提到我可能会做以下事情:

博文的发布顺序可能如下:

  1. 新的 RESTful WCF 属性
  2. 序列化选项(本文)
  3. 托管
  4. 使用 RESTful WCF 的 CRUD 操作

好了,这基本上是第 2 部分。在使用 RESTful WCF 时,您的序列化选项如下。

在本文中,我们将介绍以下序列化选项。

Message

  • DataContract
  • XML
  • 混合
  • JSON

本文不涉及如何处理 RESTful WCF 服务提供的数据资源,我们假定读者知道如何处理公开的资源。

这里有一个提示,可以看看 XLINQ 或任何其他的 XML API。

辅助方法 (Helper Methods)

在我们开始之前,我想指出附带的服务看起来是这样的:

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Runtime.Serialization;
   5:  using System.ServiceModel;
   6:  using System.ServiceModel.Channels;
   7:  using System.Text;
   8:  using System.ServiceModel.Web;
   9:  
  10:  namespace DataService
  11:  {
  12:      [ServiceContract(SessionMode = 
  13:          SessionMode.NotAllowed)]
  14:      public interface ISomeService
  15:      {
  16:  
  17:          /// <summary>
  18:          /// The OperationContracts below show how to use different 
  19:          /// Serialization techniques such as 
  20:          /// <list type="Bullet">
  21:          /// <item>Message (the most customizable/flexibility)</item>
  22:          /// <item>DataContract</item>
  23:          /// <item>XML</item>
  24:          /// <item>Hybrid approach</item>
  25:          /// </list>
  26:          /// </summary>
  27:  
  28:          [OperationContract]
  29:          [WebGet(UriTemplate = "/")]
  30:          Message GetRoot();
  31:  
  32:          [OperationContract]
  33:          [WebGet(UriTemplate = "/DC/{firstName}/{lastName}")]
  34:          Person GetRootPersonUsingDataContract(String firstName, 
  35:              String lastName);
  36:  
  37:          [OperationContract]
  38:          [WebGet(UriTemplate = "/DC/PeopleList")]
  39:          PersonList GetRootPersonListUsingDataContract();
  40:  
  41:          [OperationContract]
  42:          [XmlSerializerFormat()]
  43:          [WebGet(UriTemplate = "/XMLSer/{animalBreed}")]
  44:          Animal GetRootAnimalUsingXmlSerialization(String animalBreed);
  45:  
  46:          [OperationContract]
  47:          [XmlSerializerFormat()]
  48:          [WebGet(UriTemplate = "/XMLSer/AnimalList")]
  49:          AnimalList GetRootAnimalListUsingXmlSerialization();
  50:  
  51:  
  52:          [OperationContract]
  53:          [WebGet(UriTemplate = "/HYBRID/{firstName}/{lastName}")]
  54:          Message GetRootPersonUsingHybridStylee(String firstName,
  55:              String lastName);
  56:  
  57:          [OperationContract]
  58:          [WebGet(UriTemplate = "/HYBRID/PeopleList")]
  59:          Message GetRootPersonListUsingHybridStylee();
  60:  
  61:  
  62:          /// <summary>
  63:          /// The OperationContracts below show how to use different 
  64:          /// ResponseFormats such as XML/JSON
  65:          /// </summary>
  66:  
  67:          [OperationContract]
  68:          [WebGet(UriTemplate = "/DC/XML/{firstName}/{lastName}", 
  69:              ResponseFormat=WebMessageFormat.Xml)]
  70:          Person GetRootPersonUsingDataContractXML(String firstName, 
  71:              String lastName);
  72:  
  73:          [OperationContract]
  74:          [WebGet(UriTemplate = "/DC/XML/PeopleList", 
  75:              ResponseFormat = WebMessageFormat.Xml)]
  76:          PersonList GetRootPersonListUsingDataContractXML();
  77:  
  78:  
  79:  
  80:          [OperationContract]
  81:          [WebGet(UriTemplate = "/DC/JSON/PeopleList",
  82:              ResponseFormat = WebMessageFormat.Json)]
  83:          PersonList GetRootPersonListUsingDataContractJSON();
  84:   
  85:      }
  86:  }

服务实现使用了几个看起来是这样的辅助方法:

   1:  private Person GetPerson(
   2:      String firstName, String lastName)
   3:  {
   4:      return new Person { FirstName = firstName, LastName = lastName };
   5:  }
   6:  
   7:  private PersonList PersonList()
   8:  {
   9:      return new PersonList {
  10:          new Person { FirstName = "firstName1", LastName = "lastName1" },
  11:          new Person { FirstName = "firstName2", LastName = "lastName2" },
  12:          new Person { FirstName = "firstName3", LastName = "lastName3" },
  13:          new Person { FirstName = "firstName4", LastName = "lastName4" }
  14:      };
  15:  }

Message

Message 是 WCF 提供的所有不同序列化技术中最灵活的一种,因为它最接近用于传输 Message 的实际 Channel,并且它在自定义序列化过程方面提供了最大的自由度。作为副作用,它也需要最多的工作。

例如,当我们的服务方法如下所示时:

   1:  [OperationContract]
   2:  [WebGet(UriTemplate = "/")]
  3:  Message GetRoot();

而实际的服务实现如下所示:

   1:  //Matches [WebGet(UriTemplate = "/")]
   2:  public Message GetRoot()
   3:  {
   4:  
   5:      var stream = new MemoryStream();
   6:      XmlDictionaryWriter writer =
   7:          XmlDictionaryWriter.CreateTextWriter(stream);
   8:      writer.WriteStartDocument();
   9:      writer.WriteStartElement("Root");
  10:      writer.WriteStartElement("Hello");
  11:      writer.WriteElementString("Name", "sacha");
  12:      writer.WriteEndElement();
  13:      writer.WriteEndElement();
  14:      writer.WriteEndDocument();
  15:      writer.Flush();
  16:      stream.Position = 0;
  17:  
  18:      XmlDictionaryReader reader =
  19:          XmlDictionaryReader.CreateTextReader(
  20:          stream, XmlDictionaryReaderQuotas.Max);
  21:      return Message.CreateMessage(
  22:          MessageVersion.None, "", reader);
  23:  }

我们会得到以下结果:

35278/image-thumb2.png

这很棒,但要获取这些资源需要做很多工作,我们本可以用其他方式获取。让我们考虑一些其他选项。

DataContract

这是迄今为止最简单的方法,因为我们只依赖现有的 WCF DataContractSerializer,它为我们完成了所有繁重的工作。这种方法的问题在于,我们最终得到的 XML 可能并不总是我们想要的,而且我们对其控制力不大。所以,如果这是一个问题,您可以随时选择 Message 类型序列化或此处提供的其他选项之一。

为了演示 DataContract Serialization,我们首先需要一个实际的 DataContract 类/DataContract 集合,在附带的演示代码中,它看起来是这样的:

   1:  [DataContract()]
   2:  public class Person
   3:  {
   4:      [DataMember]
   5:      public String FirstName;
   6:  
   7:      [DataMember]
   8:      public String LastName;
   9:  
  10:  }
  11:  
  12:  
  13:  [CollectionDataContract(Name = "People", Namespace = "")]
  14:  public class PersonList : List<Person>
  15:  {
  16:  }

这样我们就可以定义一些服务方法,如下所示:

   1:  [OperationContract]
   2:  [WebGet(UriTemplate = "/DC/{firstName}/{lastName}")]
   3:  Person GetRootPersonUsingDataContract(String firstName, 
   4:      String lastName);
   5:  
   6:  [OperationContract]
   7:  [WebGet(UriTemplate = "/DC/PeopleList")]
   8:  PersonList GetRootPersonListUsingDataContract();

而实际的服务实现如下所示:

   1:  //Matches [WebGet(UriTemplate = "/DC/{firstName}/{lastName}")]
   2:  public Person GetRootPersonUsingDataContract(
   3:      String firstName, String lastName)
   4:  {
   5:      return GetPerson(firstName, lastName);
   6:  }
   7:  
   8:  
   9:  //Matches [WebGet(UriTemplate = "/DC/PeopleList")]
  10:  public PersonList GetRootPersonListUsingDataContract()
  11:  {
  12:      return PersonList();
  13:  }

这是通过浏览器运行的示例:

35278/image-thumb3.png

XML

XML 序列化已经存在了一段时间,为了使用它,我们需要用特殊属性来装饰我们的类,以辅助序列化过程。在示例代码中,我们使用了以下类:

   1:  [XmlRoot(Namespace = "", ElementName = "Animal")]
   2:  public class Animal
   3:  {
   4:      [XmlAttribute(AttributeName="breed")]
   5:      public String Breed;
   6:  }
   7:  
   8:  
   9:  [XmlRoot(Namespace = "", ElementName="Animals")]
  10:  public class AnimalList : List<Animal>
  11:  {
  12:  }

这样我们就可以定义一些服务方法,如下所示:

   1:  [OperationContract]
   2:  [XmlSerializerFormat()]
   3:  [WebGet(UriTemplate = "/XMLSer/{animalBreed}")]
   4:  Animal GetRootAnimalUsingXmlSerialization(String animalBreed);
   5:  
   6:  [OperationContract]
   7:  [XmlSerializerFormat()]
   8:  [WebGet(UriTemplate = "/XMLSer/AnimalList")]
   9:  AnimalList GetRootAnimalListUsingXmlSerialization();

而实际的服务实现如下所示:

   1:  //Matches[WebGet(UriTemplate = "/XMLSer/{animalBreed}")]
   2:  public Animal GetRootAnimalUsingXmlSerialization(String animalBreed)
   3:  {
   4:      return new Animal { Breed = animalBreed};
   5:  }
   6:  
   7:  
   8:  //Matches[WebGet(UriTemplate = "/XMLSer/AnimalList")]
   9:  public AnimalList GetRootAnimalListUsingXmlSerialization()
  10:  {
  11:      return new AnimalList {
  12:          new Animal { Breed = "breed1"},
  13:          new Animal { Breed = "breed2"},
  14:          new Animal { Breed = "breed3"},
  15:          new Animal { Breed = "breed4"}
  16:      };
  17:  }

这是通过浏览器运行的示例:

35278/image-thumb4.png

混合方法

如果您想使用强类型对象,但也想使用 Message,因为在 Message 中您可以更好地控制序列化过程,那么您可以采用一种混合方法,其工作原理大致如下。我们可以定义一些服务方法,如下所示:

   1:  [OperationContract]
   2:  [WebGet(UriTemplate = "/HYBRID/{firstName}/{lastName}")]
   3:  Message GetRootPersonUsingHybridStylee(String firstName,
   4:      String lastName);
   5:  
   6:  [OperationContract]
   7:  [WebGet(UriTemplate = "/HYBRID/PeopleList")]
   8:  Message GetRootPersonListUsingHybridStylee();

而实际的服务实现如下所示:

   1:  //Matches [WebGet(UriTemplate = "/HYBRID/{firstName}/{lastName}")]
   2:  public Message GetRootPersonUsingHybridStylee(String firstName,
   3:      String lastName)
   4:  {
   5:      Person p = GetPerson(firstName, lastName);
   6:      return Message.CreateMessage(MessageVersion.None, "*", p);
   7:  }
   8:  
   9:  
  10:  //Matches [WebGet(UriTemplate = "/HYBRID/PeopleList")]
  11:  public Message GetRootPersonListUsingHybridStylee()
  12:  {
  13:      PersonList pl = PersonList();
  14:      return Message.CreateMessage(MessageVersion.None, "*", pl);
  15:  }

使用与之前相同的辅助方法。

这种方法使我们能够使用强类型的 DataContract 对象,但使用 Messages,这使得我们可以更好地控制序列化过程,因为这是开发人员在控制,而不是由实际的 DataContractSerializers 控制。

这是通过浏览器运行的示例。

35278/image-thumb5.png

JSON

你们中的一些人可能习惯于 Web 开发,并且非常习惯(甚至喜欢,仅供参考,我不是 Web 开发人员)使用 JavaScript/手动 Ajax 调用。正如我所说,我绝对不是 Web 开发人员,但为了完整起见,我们来看看如何将一些 RESTful 资源结果序列化为 JSON。

和以前一样,大部分繁重的工作是通过一些 WCF 属性来完成的。

让我们首先看看 service interface 方法,它看起来是这样的,可以看到我们只使用了 WebMessageFormat.Json enum 来指定我们希望资源被序列化为 JSON。

   1:  [OperationContract]
   2:  [WebGet(UriTemplate = "/DC/JSON/PeopleList",
   3:      ResponseFormat = WebMessageFormat.Json)]
   4:  PersonList GetRootPersonListUsingDataContractJSON();

然后对于实际的服务实现,我们有这个:

   1:  //Matches [WebGet(UriTemplate = "/DC/JSON/PeopleList", 
   2:  //          ResponseFormat = WebMessageFormat.Json)]
   3:  public PersonList GetRootPersonListUsingDataContractJSON()
   4:  {
   5:      return PersonList();
   6:  }

使用与之前相同的辅助方法。

为了确保这能正常工作,我们可以使用一个小的示例 HTML 页面(包含在演示代码中),我只在 Internet Explorer 7 中进行了测试并使其工作。它应该能在 Firefox 中工作,但我没有花太多时间在这上面,因为这不是我想要说明的重点,我只是向你们展示 RESTful JSON 的工作原理。

   1:  <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
   2:  <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
   3:  <html>
   4:  <head>
   5:  <title>JSON Test</title>
   6:  <script type="text/javascript">
   7:  
   8:  function getXmlHttp()
   9:  {
  10:      var xmlHttp;
  11:      try {
  12:          xmlHttp = new XMLHttpRequest();
  13:      }
  14:      catch(e) {
  15:          try {
  16:              xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
  17:          }
  18:          catch (e) {
  19:              try {
  20:                  xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
  21:              }
  22:              catch (e) {
  23:                  alert("the sample only works in browsers with Ajax support");
  24:                  return false;
  25:              }
  26:          }
  27:      }
  28:  
  29:      return xmlHttp;
  30:  }
  31:  
  32:  
  33:  var serviceURI = "https://:8085/SomeService/DC/JSON/PeopleList";
  34:  
  35:  function getStuff()
  36:  {
  37:  
  38:      var xmlHttp=getXmlHttp();
  39:      xmlHttp.onreadystatechange = function() {
  40:          if (xmlHttp.readyState == 4) {
  41:              alert("xmlHttp.readyState");
  42:              var result = (eval(xmlHttp.responseText));
  43:              var person = null;
  44:              alert(result);
  45:              for (var i = 0; i < result.length; i++) {
  46:                  person = result[i];
  47:                  alert("Person found, Firstaname : " + 
  48:                  person.FirstName + " LastName : " + person.LastName);
  49:              }
  50:          }
  51:  
  52:      }
  53:      xmlHttp.open("GET", serviceURI, true);
  54:      xmlHttp.setRequestHeader("Accept","application/json");
  55:      xmlHttp.send(null);
  56:      
  57:  }
  58:  
  59:  </script>
  60:  
  61:  </head>
  62:  
  63:  <body >
  64:  <form name="form1" action="javascript:getStuff()">
  65:  <input type="submit" value="Enter"> </form>
  66:  </body>
  67:  </html>

这是运行的演示(同样针对随附的演示代码中包含的简单 WCF 服务控制台宿主应用程序)。

35278/image-thumb6.png

正如我所说,我不是 Web 开发人员,但为了完整起见,我将在后续的文章中向您展示如何在 ASP .NET/IIS 中托管 RESTFul WCF 服务,以及如何使用 ASP .NET AJAX ScriptManager 对象。对我来说,如果我喜欢 Web 开发,这应该是您想要做的方式,因为这样您就可以避免编写所有这些糟糕的 JavaScript AJAX 代码。哎呀。那简直不酷。我知道 ASP .NET AJAX 很庞大,但拜托,那个 xmlHttp.onreadystatechange 太恶心了。

© . All rights reserved.