第九讲:消息契约
代码
https://yunpan.cn/cPns5DkGnRGNs 密码:3913
消息交换是WCF进行通信的唯一手段,通过方法调用形式体现的服务访问需要转化成具体的消息,并通过相应的编码才能经过传输通道发送到服务端;服务操作执行的结果也只能以消息的形式才能被正常地返回客户端。所以,消息在整个WCF体系结构中处于一个核心的地位,WCF可以看成是一个消息处理的管道。
尽管消息在整个WCF体系中具有如此重要的意义,可是我们却意识不到消息的存在。原因很简单,WCF设计的目标就是实现消息通信的所有细节,为最终的编程人员提供一个完全面向对象的编程模型。所以对于一般的编程人员来说,他们面对的是接口,却不知道服务契约对于服务的描述;面对的是数据类型,却不知道数据契约对序列化的作用;面对的是方法调用和返回值的获取,却不了解底层消息交换的过程。
从本质上讲,消息就是基于某种标准化的结构对于数据的封装。从数据的表现形式来看,XML由于具有强大的表意能力和平台无关的特性,已经成为事实上的数据表现和传输的标准,所以WCF在设计之处就是以XML为中心的。但是近年来AJAX的盛行,WCF的消息为另一种非XML的格式数据的表现形式也提供了支持,那就是JSON.
对于基于XML的消息来说,SOAP是一种主要的表现形式,随着SOAP规范(SOAP 1.1和SOAP1.2)标准的不断完善,以及SOA不断深入人心,SOAP已经成为一种被业界普遍接受和使用的消息交换协议。
SOA强调以消息为中心的应用设计,消息交换是SOA最基本的行为,所以需要一种规范对消息的格式进行标准化。对于一个真正企业级的应用,消息交换不仅局限于业务相关数据的传递,还承载着一些非业务功能的实现,比如安全,可靠消息通信,事务的流转等。这就为消息的可扩展性提出了一新的要求,而SOAP满足这样的要求。
按照最新的SOAP的规范(SOAP1.2)的定义,SOAP是一个轻量的协议,用以规范在一个分布式环境下进行结构化的信息交换。整个SOAP1.2规范基本上分为两个部分:第一部分为消息框架,主要介绍基本的SOAP处理模型、SOAP的可扩展模型、SOAP协议绑定框架、SOAP消息的结构等;第二部分为附件,用以规定一些在消息框架中漏掉的附属规范,比如SOAP数据模型、SOAP编码等。
对于一个消息通信框架来说,SOAP规范了消息的格式,解决了如何用消息表示消息的问题。
WS-Addressing
WS-寻址(WS-Addressing)为基于消息通信的寻址定了规范。Ws-Addressing提供了一种与传输无关的机制,实现了WEB服务和SOAP消息寻址问题。
WS-Addressing的核心大体可以分为两个部分:终结点引用和消息寻址属性
1.终结点引用:
消息交换的参与者是终结点,消息从一个终结点发送到另一个终结点。所以要进行正常的消息交换,需要解决的是终结点引用的问题。
WS-Addressing对一个终结点引用作了相应的规范,规定一个终结点引用有如下3种类型的属性构成:
1).Address:每个终结点引用必须包含一个(也是唯一一个)通过绝对URI表示的地址,该地址即为终结点的地址。
2).Reference Parameters:每一个终结点引用可以包含零到多个引用参数。每个引用参数可以看成是对终结点的辅助性描述,以利于更好地进行终结点交互。
3).Metadata:每一个终结点引用可以包含零到多个元数据。
2.消息寻址属性:
为了保障正常的消息交换,我们往往需要一些必要的寻址方面的信息,用以定位相应的终结点。比如请求消息发送的原始终点地址,回复消息发送的目标终结点地址,错误消息发送的目标终结点地址等。
当你定义服务契约时,通常,你会描述一个操作集,可以包含参数列表和返回值。每一个操作都会最终被链接到一个请求或响应消息上,或者在服务描述中同时链接到两者之上。这取决于操作的消息交换模式。消息契约是一个更加规范的方法,用以描述操作消息。它提供了在消息现结构之上的更细粒度的控制。这一点对于WEB服务机制尤为重要。你可以使用消息契约作为唯一参数并作为任何操作的返回类型,以代替参数列表和返回值(包括数据契约或串行化类型)。
数据契约对消息契约来说仍然十分重要。事实上,消息契约内在地描述了参数列表和返回值将由什么组成。由消息契约所添加的值被用来对消息标题和消息体元素如何被串行化实施更大的控制。
我们直接上Demo 代码在云盘
代码是根据 第八讲 数据契约版本控制 的Demo 修改,我们这里只贴出 变动的地方,不变的地方不再说明:
先看结构:
[ 9-01 ]
变动的地方
1.Form1 窗体的代码(客户端)
2.GigManager项目下IGigManagerService.cs 类(服务契约)
3.GigManager项目下 新增 Messages.cs 类(消息契约)
首先我们先添加 消息契约 Messages:
定义相关的消息契约,消息分 消息头 ,消息体
[ 9-02 ]
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using GigEntry.localhost; 10 11 namespace GigEntry 12 { 13 public partial class Form1 : Form 14 { 15 localhost.GigManagerServiceContractClient proxy = new GigManagerServiceContractClient(); 16 public Form1() 17 { 18 InitializeComponent(); 19 20 } 21 private void cmdSave_Click(object sender, EventArgs e) 22 { 23 ListItem item = new ListItem(); 24 item.Id = int.Parse(this.txtId.Text); 25 item.Title = this.txtTitle.Text; 26 item.Description = this.txtDescription.Text; 27 item.DateStart = this.dtpStart.Value; 28 item.DateEnd = this.dtpEnd.Value; 29 item.Url = this.txtUrl.Text; 30 proxy.SaveGig(item); 31 32 } 33 private void cmdGet_Click(object sender, EventArgs e) 34 { 35 ListItem item = proxy.GetGig("XXX"); 36 if (item != null) 37 { 38 this.txtId.Text = item.Id.ToString(); 39 this.txtTitle.Text = item.Title; 40 this.txtDescription.Text = item.Description; 41 this.dtpStart.Value = item.DateStart; 42 this.dtpEnd.Value = item.DateEnd; 43 this.txtUrl.Text = item.Url; 44 } 45 } 46 } 47 }
下面 修改 服务契约 以及 它的实现,看我们如何 将消息契约作为唯一参数并作为任何操作的返回类型,以代替参数列表和返回值 :
[ 9-03 ]
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.ServiceModel; 6 using ContentTypes; 7 8 namespace GigManager 9 { 10 11 [ServiceContract(Name = "GigManagerServiceContract", Namespace = "http://www.HulkXu.com/")] 12 public interface IGigManagerService 13 { 14 [OperationContract] 15 SaveGigResponse SaveGig(SaveGigRequest saveGigRequest); 16 17 [OperationContract] 18 GetGigResponse GetGig(GetGigRequest getGigRequest); 19 20 } 21 22 public class GigManagerService : IGigManagerService 23 { 24 25 private ListItem m_listItem; 26 27 public SaveGigResponse SaveGig(SaveGigRequest saveGigRequest) 28 { 29 m_listItem = saveGigRequest.Item; 30 return new SaveGigResponse(); 31 } 32 33 public GetGigResponse GetGig(GetGigRequest getGigRequest) 34 { 35 //这里 消息的头 是不是 三个 X ,如果不是 ,则抛出异常信息 36 if (getGigRequest.LicenseKy.ToLower() != string.Format("XXX").ToLower()) 37 { 38 throw new FaultException("invalid License Key"); 39 } 40 return new GetGigResponse(m_listItem); 41 } 42 } 43 44 }
最后我们修改下 From1窗体的代码:
[ 9-04 ]
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using GigEntry.localhost; 10 11 namespace GigEntry 12 { 13 public partial class Form1 : Form 14 { 15 localhost.GigManagerServiceContractClient proxy = new GigManagerServiceContractClient(); 16 public Form1() 17 { 18 InitializeComponent(); 19 20 } 21 private void cmdSave_Click(object sender, EventArgs e) 22 { 23 ListItem item = new ListItem(); 24 item.Id = int.Parse(this.txtId.Text); 25 item.Title = this.txtTitle.Text; 26 item.Description = this.txtDescription.Text; 27 item.DateStart = this.dtpStart.Value; 28 item.DateEnd = this.dtpEnd.Value; 29 item.Url = this.txtUrl.Text; 30 proxy.SaveGig(item); 31 32 } 33 private void cmdGet_Click(object sender, EventArgs e) 34 { 35 ListItem item = proxy.GetGig("XXX"); 36 if (item != null) 37 { 38 this.txtId.Text = item.Id.ToString(); 39 this.txtTitle.Text = item.Title; 40 this.txtDescription.Text = item.Description; 41 this.dtpStart.Value = item.DateStart; 42 this.dtpEnd.Value = item.DateEnd; 43 this.txtUrl.Text = item.Url; 44 } 45 } 46 } 47 }
好了,完成。
运行一下测试成功。
消息契约和SOAP
服务操作由服务契约中的方法签名来定义,通常作为接口定义的一部分。每一个服务操作描述了客户需要发送的参数和它们将会接受的返回值(如果有的话).然而,当用XML格式来表达时,参数和返回值实际上是被包装在一个SOAP消息体中。当你使用消息契约作为操作参数和返回值时,你事实上就获得了整个SOAP信封的控制权,同时具有了定义定制消息标题和单个体元素的能力。