WCF初探-19:WCF消息协定
WCF消息协定概述
- 在生成 WCF应用程序时,开发人员通常会密切关注数据结构和序列化问题,而不必关心携带数据的消息结构。 对于这些应用程序,为参数或返回值创建数据协定的过程很简单。但是,有时完全控制 SOAP 消息的结构与控制其内容一样重要。 当必须提供互操作性或需要在消息或消息部分级别特别控制安全问题时,更是如此。 在这些情况下,您可以创建消息协定 ,使您可以指定所需的精确的 SOAP 消息的结构。
- 一般来说,定义消息的架构时使用数据协定就足够了。但是,有时必须精确控制如何到通过网络传输的 SOAP 消息的结构。对于这种情况,最常见的方案是插入自定义 SOAP 标头。另一种常见方案是定义消息头和正文的安全属性,也就是说,确定是否对这些元素进行数字签名和加密。消息样式的操作可提供这种控制。
创建消息协定
- 消息样式的操作最多具有一个参数和一个返回值,其中参数和返回值的类型都是消息类型;也就是说,这两种类型可直接序列化为指定的 SOAP 消息结构。这可以是用 MessageContractAttribute 标记的任何类型或 Message 类型。
- 若要为某一类型定义消息协定(即定义该类型和 SOAP 信封之间的映射),请对该类型应用 MessageContractAttribute。 然后对该类型中要成为 SOAP 标头的成员应用 MessageHeaderAttribute,并对要成为消息的 SOAP 正文部分的成员应用 MessageBodyMemberAttribute。
- 可以对所有字段、属性和事件应用 MessageHeaderAttribute 和 MessageBodyMemberAttribute,而不管这些字段、属性和事件是公用的、私有的、受保护的还是内部的。
WCF消息协定示例
- 解决方法结构如下:
- 工程结构说明:
- Service:类库程序,WCF服务端程序。在服务协定接口IuserInfo.cs中定义了消息协定User类型的类,其中属性OprationType和OperationTime定义为消息标头,其他属性定义为消息正文。定义操作协定GetInfo(),返回消息协定User。IUserInfo.cs代码如下:
using System; using System.ServiceModel; using System.Collections.Generic; using System.Runtime.Serialization; namespace Service { [ServiceContract] public interface IUserInfo { [OperationContract] User GetInfo(); } [MessageContract] public class User { [MessageHeader] public string OprationType { get; set; } [MessageHeader] public DateTime OperationTime { get; set; } [MessageBodyMember] public int ID { get; set; } [MessageBodyMember] public string Name { get; set; } [MessageBodyMember] public int Age { get; set; } [MessageBodyMember] public string Nationality { get; set; } } }
UserInfo.cs代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Service { public class UserInfo:IUserInfo { public User GetInfo() { User user = new User(); user.OprationType = "GET"; user.OperationTime = System.DateTime.Now; user.Name = "JACK"; user.Age = 20; user.ID = 1; user.Nationality = "CHINA"; return user; } } }
2. Host:控制台应用程序,服务承载程序。添加对程序集Service的引用,完成以下代码,寄宿服务。Program.cs代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Service; using System.ServiceModel; namespace Host { class Program { static void Main(string[] args) { using (ServiceHost host = new ServiceHost(typeof(UserInfo))) { host.Opened += delegate { Console.WriteLine("服务已经启动,按任意键终止!"); }; host.Open(); Console.Read(); } } } }
App.config代码如下:
<?xml version="1.0"?> <configuration> <system.serviceModel> <services> <service name="Service.UserInfo" behaviorConfiguration="mexBehavior"> <host> <baseAddresses> <add baseAddress="http://localhost:1234/UserInfo/"/> </baseAddresses> </host> <endpoint address="" binding="wsHttpBinding" contract="Service.IUserInfo" /> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> </service> </services> <behaviors> <serviceBehaviors> <behavior name="mexBehavior"> <serviceMetadata httpGetEnabled="true"/> <serviceDebug includeExceptionDetailInFaults="true"/> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> </configuration>
我们通过svcutil.exe工具生成客户端代理类和客户端的配置文件,注意,此处生成的客户端代理类型为消息协定类型,所以需要在svcutil.exe的参数后追加/mc才能生成消息协定的客户端代理类。
svcutil.exe是一个命令行工具,位于路径C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin下,我们可以通过命令行运行该工具生成客户端代理类
- 在运行中输入cmd打开命令行,输入 cd C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin
- 输入svcutil.exe /out:f:\UserInfoClient.cs /config:f:\App.config http://localhost:1234/UserInfo /mc
3. Client:控制台应用程序,客户端调用程序。将生成的UserInfoClient.cs和App.config复制到Client的工程目录下,完成客户端调用代码。Program.cs的代码如下:
using System; namespace Client { class Program { static void Main(string[] args) { UserInfoClient proxy = new UserInfoClient(); User user = proxy.GetInfo(new GetInfoRequest()); Console.WriteLine("{0,-10}{1,-20}{2,-10}{3,-10}{4,-10}{5,-10}", "Type","Time","ID", "Name", "Age", "Nationality"); Console.WriteLine("{0,-10}{1,-20}{2,-10}{3,-10}{4,-10}{5,-10}", user.OprationType.ToString(), user.OperationTime.ToString(), user.ID.ToString(), user.Name.ToString(), user.Age.ToString(), user.Nationality.ToString()); Console.Read(); } } }
程序运行结果如下:
总结:
- 通过示例,我们完成了对消息协定的创建和调用。接下来我们可以利用WCF客户端测试程序来查看消息协定存在于消息中的位置。在开始菜单中找到visual studio 2010的安装目录Visual studio Tools中的visual studio命令提示工具,输入wcftestclient命令,在WCF客户端测试中添加服务地址http://localhost:1234/UserInfo/引用,添加成功后,点击GetInfo方法的调用按钮,查看调用返回的消息。在其中,我们看到了属性OprationType和OperationTime显示在了消息头的部分,而其他的属性显示在了消息正文的部分。
-
在生成消息客户端代理类时,我们追加了一个/mc的参数,当我们查看客户端代理类UserInfoClient.cs的代码时会发现,客户端生成的消息协定按照服务端消息协定的定义被MessageContractAttribute和MessageHeaderAttribute以及MessageBodyMemberAttribute修饰了。但是操作协定的GetInfo却多出了一个类型为GetInfoRequest参数。所以我们在客户端调用的时候,还要new 一个 GetInfoRequest参数类型,才能调用GetInfo方法。