JSON 启用 WCF 服务 - 第一部分






4.79/5 (13投票s)
创建 WCF 服务并将其配置为使用 JSON 格式的数据。
引言
本文描述了 WCF 服务配置以 JSON 格式处理数据的过程。尽管配置过程看起来很简单,但有些细节不容忽视。
背景
作为一家跨国软件公司的首席开发人员,我经常开发富互联网应用程序,并且非常关注确保应用程序能够长期稳定运行。最近,在选择合适的技术方面,我遇到了一个真正的难题。一方面,最常用的两种是 Silverlight 和 Flash。它们都是跨平台技术,因此用 Silverlight 或 Flash 创建的应用程序在所有浏览器中看起来都一样。
另一方面,恕我直言,这些技术有一个严重的缺点:它们的使用受到限制。您不能在 Linux 上使用 Silverlight 应用程序,也不能在 iOS 上使用 Flash 应用程序。我甚至没有提到对我们生活至关重要的移动系统。大多数现代移动设备和平板电脑对 Silverlight 和 Flash 的支持并不完全。
使用 HTML 和 JavaScript 可以消除兼容性问题。使用此类应用程序只需要任何设备上的 Web 浏览器。
我不再过多地讨论不同技术的利弊——我全部使用它们,并且决定通常取决于具体项目。但是今天我想告诉您,在我目前参与的一个项目中,我为什么决定选择 HTML 和 JavaScript 而不是其他技术。
创建 WCF 服务
在我们的一项项目中,我决定使用 HTML+JavaScript 作为应用程序的客户端部分。客户端应用程序与服务器交互以获取数据。可以使用 WCF 服务、MVC 框架控制器和 Web Handlers 作为服务。在我们的例子中,我们需要使用一种通用的数据格式,客户端应用程序和服务器将在此格式下进行交互。因此,我们必须选择使用 XML 还是 JSON。经过一些测试和考虑,我们选择了 JSON。我们选择 JSON 有几个原因。
现在,我想告诉您我们如何创建传递 JSON 格式数据的 WCF 服务。
第一步是将 WCF 服务添加到网站。
在“添加新项”对话框窗口中选择 WCF 服务(我将服务命名为 WcfJsonService)。之后,应用程序将添加 WcfJsonService.svc 文件以及两个文件:IWcfJsonService.cs 和 WcfJsonService.svc.cs。
让我更深入地了解添加到项目中的这些文件。IWcfJsonService.cs 文件定义了服务实现的接口。它包含指定 WCF 如何使用该服务的属性。服务的实现描述在代码隐藏文件(WcfJsonService.svc.cs)中。此文件已添加到 .svc 文件中。您可以使用这两个文件,但我倾向于删除它们并创建一个类库。该库包含接口和服务实现。因此,由于以下原因,我们可以更方便地管理我们 WCF 服务的代码:
- 添加实现服务接口的其他提供程序时的灵活性;
- 易于创建单元测试。
接下来,您将看到该方法不会使开发复杂化,它只需要添加对类库的引用。
更改服务声明文件
我们需要更改 .svc 文件以正确实现我们的服务。以下是默认在 .svc 文件中创建的指令示例
<%@ ServiceHost Language="C#" Debug="true" Service="WebApplication.WcfJsonService"
CodeBehind="WcfJsonService.svc.cs" %>
下面,您可以看到新版本的指令。此指令指示哪个类用于服务实现。WebScriptServiceHostFactory
(http://msdn.microsoft.com/en-us/library/bb336135.aspx)类被指定为工厂。此类允许使用 Web 协议并添加服务与 jQuery AJAX 配合工作所需的 JSON 序列化。
<%@ ServiceHost Language="C#" Debug="true"
Service="WCFImplementation.ContinentsPopulation"
Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory"%>
创建服务接口
下一步是添加一个用于服务实现的类。我添加的第一个文件是服务接口。在我的示例中,我显示了包含人口数量的大陆列表。我将此服务接口命名为 IContinentsPopulation.cs。现在有必要定义服务的契约。我用 ServiceContract
属性标记我的接口。WCF 属性位于 System.ServiceModel
命名空间中,我需要添加对此的引用。
[ServiceContract]
public interface IContinentsPopulation
现在我可以在接口中添加方法签名。我为应用程序添加了两个方法
GetContinents
,用于获取包含人口的大陆列表;GetContinentDetails
,用于返回指定名称的大陆的数据。
下面您可以看到方法的描述
[OperationContract(Name = "GetContinents")]
ContinentsListResponse GetContinents();
[OperationContract(Name = "GetContinentDetails")]
ContinentDetailsRespnse GetContinentDetails(ContinentDetailRequest request);
实现服务
我可以添加一个用于服务实现的类。我创建了 ContinentsPopulation.cs 文件。ContinentsPopulation
类实现了 IContinentsPopulation
接口。它用 ServiceBehavior
属性进行描述。在我的示例中,IncludeExceptionDetailInFaults
属性设置为 true
。我特意这样做是为了让所有异常消息都发送到客户端并以所需的形式进行序列化。在我的情况下,jQuery 获取 JSON 数据。
现在,我应该有对不存在的类的引用。它们是 ContinentsListResponse
、ContinentDetailsResponse
和 ContinentDetailRequest
。但首先我需要创建它们。这些类都是消息类。ContinentsListResponse
和 ContinentDetailsResponse
类是服务器和客户端之间消息的消息类。更具体地说,它们是包含服务器响应数据的类。ContinentDetailRequest
类是从客户端到服务器的消息类,它包含服务器响应数据。
现在让我们创建一个单独的类库。该库允许将数据模型与服务实现分离。我将我的类添加到其中。此外,在实现客户端部分时,我可以在其他项目中重用消息类。
消息类使用 WCF 进行序列化。因此,我需要将 DataContract
属性添加到类,并将 DataMember
属性添加到类的每个成员。成员名称在此属性中定义。然后我需要指定此值是必需的:IsRequired=true
。
最后,我需要指定数据定位顺序:Order = 0。这意味着该值将是序列化对象中的第一个或索引为 0 的值。
DataContract
和 DataMember
属性位于 System.Runtime.Serialization
命名空间中,我需要添加对此的引用。
消息类如下所示。它们包含大陆名称和人口数量值
[cc lang="Csharp" escaped="true"][DataContract(Name = "ContinentPopulation")]
public class ContinentPopulation
{
[DataMember(Name = "ContinentName",IsRequired = true,Order = 0)]
public string ContinentName { get; set; }
[DataMember(Name = "TotalPopulation", IsRequired = true, Order = 1)]
public int TotalPopulation { get; set; }
}
[/cc]
它包含大陆的详细数据:大陆名称、面积、占陆地总面积的百分比、人口数量值以及占地球总人口的百分比。
[cc lang="Csharp" escaped="true"][DataContract(Name = "ContinentDetails")]
public class ContinentDetails
{
[DataMember(Name = "ContinentName", IsRequired = true, Order = 0)]
public string ContinentName { get; set; }
[DataMember(Name = "Area", IsRequired = true, Order = 1)]
public long Area { get; set; }
[DataMember(Name = "PercentOfTotalLandmass", IsRequired = true, Order = 2)]
public long PercentOfTotalLandmass { get; set; }
[DataMember(Name = "TotalPopulation", IsRequired = true, Order = 3)]
public long TotalPopulation { get; set; }
[DataMember(Name = "PercentOfTotalPopulation", IsRequired = true, Order = 4)]
public long PercentOfTotalPopulation { get; set; }
}[/cc]
它包含需要获取详细数据的大陆名称。
[cc lang="Csharp" escaped="true"][DataContract(Name = "ContinentDetailRequest")]
public class ContinentDetailRequest
{
[DataMember(Name = "ContinentName", IsRequired = true, Order = 0)]
public string ContinentName { get; set; }
}
[/cc]
它是一个表示服务器返回的大陆详细数据的类。
[cc lang="Csharp" escaped="true"][DataContract(Name = "ContinentDetailsResponse")]
public class ContinentDetailsResponse
{
[DataMember(Name = "Details", IsRequired = true, Order = 0)]
public ContinentDetails Details { get; set; }
}
[/cc]
它是一个表示服务器返回的包含大陆列表和人口数量的响应的类。
[cc lang="Csharp" escaped="true"][DataContract(Name = "ContinentsListResponse")]
public class ContinentsListResponse
{
[DataMember(Name = "Continents", IsRequired = true, Order = 0)]
public List Continents { get; set; }
}[/cc]
在实际项目中,服务器获取的数据来自数据库。我不会通过创建数据库连接类并在代码中填充数据来使我的示例复杂化。我在类中创建了私有字段 Continents
。在创建服务实例时初始化此字段,并在 InitializeData
方法中填充它。
下面是 ContinentsPopulation
类的实现
[cc lang="Csharp" escaped="true"][ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class ContinentsPopulation : IContinentsPopulation
{
private List continents = null;
public ContinentsPopulation()
{
continents = new List();
InitializeData();
}
private void InitializeData()
{
continents.Add(new ContinentDetails()
{
ContinentName = "Asia",
Area = 43820000,
PercentOfTotalLandmass = 29.5,
TotalPopulation = 3879000000,
PercentOfTotalPopulation = 60,
});
continents.Add(new ContinentDetails()
{
ContinentName = "Africa",
Area = 30370000,
PercentOfTotalLandmass = 20.4,
TotalPopulation = 922011000,
PercentOfTotalPopulation = 14,
});
continents.Add(new ContinentDetails()
{
ContinentName = "North America",
Area = 24490000,
PercentOfTotalLandmass = 16.5,
TotalPopulation = 528720588,
PercentOfTotalPopulation = 8,
});
continents.Add(new ContinentDetails()
{
ContinentName = "South America",
Area = 17840000,
PercentOfTotalLandmass = 12,
TotalPopulation = 382000000,
PercentOfTotalPopulation = 6,
});
continents.Add(new ContinentDetails()
{
ContinentName = "Antarctica",
Area = 13720000,
PercentOfTotalLandmass = 9.2,
TotalPopulation = 1000,
PercentOfTotalPopulation = 0.00002,
});
continents.Add(new ContinentDetails()
{
ContinentName = "Europe",
Area = 10180000,
PercentOfTotalLandmass = 6.8,
TotalPopulation = 731000000,
PercentOfTotalPopulation = 11.5,
});
continents.Add(new ContinentDetails()
{
ContinentName = "Australia",
Area = 9008500,
PercentOfTotalLandmass = 5.9,
TotalPopulation = 31260000,
PercentOfTotalPopulation = 0.5,
});
}
public ContinentsListResponse GetContinents()
{
return new ContinentsListResponse()
{
Continents = continents.Select(p =>
new ContinentPopulation
{
ContinentName = p.ContinentName,
TotalPopulation = p.TotalPopulation
}).ToList()
};
}
public ContinentDetailsResponse GetContinentDetails(ContinentDetailRequest request)
{
ContinentDetails continentDetails =
continents.First(p => p.ContinentName.Equals(request.ContinentName));
return new ContinentDetailsResponse()
{
Details = new ContinentDetails
{
Area = continentDetails.Area,
ContinentName = continentDetails.ContinentName,
PercentOfTotalLandmass = continentDetails.PercentOfTotalLandmass,
PercentOfTotalPopulation = continentDetails.PercentOfTotalPopulation,
TotalPopulation = continentDetails.TotalPopulation,
}
};
}
}[/cc]
结论
现在我们有了一个 WCF 服务,它可以获取和返回可以在应用程序客户端使用的 JSON 对象。您可以轻松地将此类服务配置为在您的应用程序中使用。以 JSON 格式传递和接收数据的 WCF 服务是通用的,可用于 Web 和 Windows 窗体应用程序。在下一篇文章中,我想介绍使用 JSON 与 WCF 服务进行 AJAX 调用的基础设施和实现。