基于SOAP消息格式的WCF之所以强大原因之一是因为SOAP消息头的高度扩展性。相应的WS-*协议很多都体现在消息头封装的信息上,包括诸如寻址,需要调用方法名,维护Session的信息等等……

SOAP示例

  1. <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing"
    xmlns:s="http://www.w3.org/2003/05/soap-envelope">
  2.  
  3. 下面就是很长很有内涵的消息头
    <s:Header>
    <a:Action s:mustUnderstand="1" u:Id="_2" xmlns:u="http://docs.oasis-
    open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
    http://www.thatindigogirl.com/samples/2006/06/PhotoUploadContract/UploadPhoto
    </a:Action>
    <a:MessageID u:Id="_3" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-
    200401-wss-wssecurity-utility-1.0.xsd">
    urn:uuid:940d5687-fcb2-44b5-a696-cc7eba22524b</a:MessageID>
    <a:ReplyTo u:Id="_4" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-
    200401-wss-wssecurity-utility-1.0.xsd">
    <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
    </a:ReplyTo>
    <a:To s:mustUnderstand="1" u:Id="_5" xmlns:u="http://docs.oasis-
    open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
    http://localhost:3045/PhotoApplication/PhotoManagerService.svc/Soap12</a:To>
    <o:Security .../>
    </s:Header>
  4.  
  5. Body部分省略
    <s:Body>........</s:Body>
  6.  
  7. </s:Envelope>

我们要清楚的是,SOAP协议规定的只是一种带有固定格式的XML。规定了由消息头和消息体两部分组成,并且规定了相应的节点。

WCF中的REST是一种POX(plain old xml),换句话说就是没有任何规定的xml,没有强行规定消息头,没有消息体。甚至因为没有规定,所以传输的格式用不用xml都不是大问题,Json也可以做为传输数据的封装格式。

没有消息头,所以我们需要另外一种方式定位所需调用的方法,传递我们需要传送的参数信息。而我们能依靠的,就是Http协议给我们提供的信息。

总的来说,基于SOAP消息的WCF是对于WS-*规范的一种实现。而REST方式的WCF和采用basicHttpBinding差不多,也就是什么都没有,相应的规范则由我们自己定义和实现。

我们都知道(额,不知道的话以后再说)如果调用基于SOAP消息的WCF服务的客户端也是WCF的话,那么客户端就可以使用透明代理,在通过反射把对方法栈的调用拦截并转换为相应的消息,发送给服务提供者。其中相应的调用方法信息则封装在action属性里。服务端通过消息过滤器查找到相应的信息,并实例化service对象(服务对象也有可能已经实例化好了,具体看你的InstanceContextMode),调用相应的方法。

为了方便演示,我直接通过VS自带的模板新建了一个WCF Liberary项目用来说明上面描述的步骤。

图一:Contract的定义

  1.     [ServiceContract]
  2.     public interface IService1
  3.     {
  4.         [OperationContract]
  5.         CompositeType GetDataUsingDataContract(CompositeType composite);
  6.  
  7.     }

图2:根据Contract信息生成的元数据,包含相应的Action节点,用来告知客户端如何序列化相应的SOAP消息头:

  1. <wsdl:portType name="IService1">
    <wsdl:operation name="GetDataUsingDataContract">
    <wsdl:input wsaw:Action="http://tempuri.org/IService1/GetDataUsingDataContract" message="tns:IService1_GetDataUsingDataContract_InputMessage"/>
    <wsdl:output wsaw:Action="http://tempuri.org/IService1/GetDataUsingDataContractResponse" message="tns:IService1_GetDataUsingDataContract_OutputMessage"/>
    </wsdl:operation>
    </wsdl:portType>
 

图3:在客户端调用方法时,透明代理把对方法的调用拦截并转换SOAP消息发送给服务端,下图就是转换后发出的SOAP:

  1. <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
    <s:Header>
      注意下面这一行
    <a:Action s:mustUnderstand="1">http://tempuri.org/IService1/GetDataUsingDataContract</a:Action>
    <a:MessageID>urn:uuid:2b959c1a-a159-4d25-bbb0-9f644b92fa68</a:MessageID>
    <a:ReplyTo>
    <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
    </a:ReplyTo>
    </s:Header>
    <s:Body>
    <GetDataUsingDataContract xmlns="http://tempuri.org/">
    <composite xmlns:d4p1="http://schemas.datacontract.org/2004/07/WcfServiceLibrary1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <d4p1:BoolValue>true</d4p1:BoolValue>
    <d4p1:StringValue>df</d4p1:StringValue>
    </composite>
    </GetDataUsingDataContract>
    </s:Body>
    </s:Envelope>


现在问题出来了,如果我们不是通过相应的序列化器,而是选择自己发送没有相应消息头的XML,比如:

  1. <GetDataUsingDataContract xmlns="http://tempuri.org/">
    <composite>
    <d4p1:BoolValue>true</d4p1:BoolValue>
    <d4p1:StringValue>df</d4p1:StringValue>
    </composite>
    </GetDataUsingDataContract>

或者干脆就是在浏览器输入一个地址,当然也就没有action,会出现什么问题?

我们最直接的反应应该是,服务端找不到相应的信息,报错:

  1. <Fault><Code><Value>Sender</Value><Subcode><Value>a:ActionNotSupported</Value></Subcode></Code><Reason><Text xml:lang="zh-CN">由于 ContractFilter 在 EndpointDispatcher 不匹配,因此 Action 为“”的消息无法在接收方处理。这可能是由于协定不匹配(发送方和接收方 Action 不匹配)或发送方和接收方绑定/安全不匹配。请检查发送方和接收方是否具有相同的协定和绑定(包括安全要求,如 Message、Transport、None)。</Text></Reason></Fault>

因此我们的OperationContract特性需要修改一下,处理那些找不到调用信息的请求:

为了方便说明,因此我新写了一个方法。

  1. //把[OperationContract] 注释掉,改成下面的写法
    [OperationContract(Action = "*", ReplyAction = "*")]
    Message Resource(Message input);

这样所有由客户端发送过来,没有被处理的消息都会被送到这里进行处理,因此我们就可以处理那些不带消息头的信息了。以Message类作为参数是为了方便使用Http中所包含的信息,因为可以方便的转换为HttpRequestMessageProperty 类型。下面这个方法实现的简单功能就是根据参数信息获取相应的文件。比如访问http://localhost:9527/Service?job ,获取job.xml的信息,此时没有消息头并且也不再需要消息头筛选:

  1.         public Message Resource(Message input)
  2.         {
  3.             HttpRequestMessageProperty httpRequestProperty = (HttpRequestMessageProperty)input.Properties[HttpRequestMessageProperty.Name];
  4.             string query = httpRequestProperty.QueryString;
  5.             string fileName = string.Format("{0}.xml", query);
  6.             switch (httpRequestProperty.Method)
  7.             {
  8.                 case "GET":
  9.                     Message message = null;
  10.                     if (File.Exists(fileName))
  11.                     {
  12.                         XmlDocument document = new XmlDocument();
  13.                         document.Load(fileName);
  14.                         message = Message.CreateMessage(
  15.                         MessageVersion.None,
  16.                         "*",
  17.                         new XmlNodeReader(document.DocumentElement));
  18.                     }
  19.                     else
  20.                     {
  21.                         message = Message.CreateMessage(
  22.                         MessageVersion.None,
  23.                         "*");
  24.                     }
  25.                     return message;
  26.                 default:
  27.                     return Message.CreateMessage(
  28.                     MessageVersion.None, "*");
  29.             }
  30.         }
  31.  
  32. 相应的配置文件,不用多说了吧,既然采用的是REST,那么编码方式肯定就是文本,传输方式肯定就是Http。注意MessageVersion = None意味着咱们不需要对输入的数据的格式有要求
  1. <services>
    <service name="Service.SimpleServer">
    <endpoint address="Service" binding="customBinding" bindingConfiguration="RESTPOXBinding"
    contract="IService.ISimpleResource" />
    <host>
    <baseAddresses>
    <add baseAddress="http://localhost:9527/" />
    </baseAddresses>
    </host>
    </service>
    </services>
    <bindings>
    <customBinding>
    <binding name="RESTPOXBinding">
    <textMessageEncoding messageVersion="None"/>
    <httpTransport/>
    </binding>
    </customBinding>
    </bindings>
  1. 当然,这么写是有点小麻烦,WCF的设计者肯定会考虑到这点,帮我们采用URI模板方式封装好了,对比:
  2.  
  3. 原始方式:
  1. [OperationContract(Action = "*", ReplyAction = "*")]
    Message Resource(Message input);
  1. 新的方式:
  1. [OperationContract]
    [WebGet(UriTemplate = “/{query}”, BodyStyle = WebMessageBodyStyle.Bare)]
    Message Resource(string query);
  2.  
  3. 这样我们在实现方法的时候就不用再自己手动调用 HttpRequestMessageProperty 类提取信息,也不再判断Method类型,因此重写后的Resource方法看起来也清爽了很多。
  1.         public Message Resource(String query)
  2.         {
  3.             string fileName = string.Format("{0}.xml", query);
  4.                     Message message = null;
  5.                     if (File.Exists(fileName))
  6.                     {
  7.                         XmlDocument document = new XmlDocument();
  8.                         document.Load(fileName);
  9.                         message = Message.CreateMessage(
  10.                         MessageVersion.None,
  11.                         "*",
  12.                         new XmlNodeReader(document.DocumentElement));
  13.                     }
  14.                     else
  15.                     {
  16.                         message = Message.CreateMessage(
  17.                         MessageVersion.None,
  18.                         "*");
  19.                     }
  20.                     return message;
  21.         }
  22.  
  23. 同样的,系统给了我们一个webHttpBinding套餐,也是对上面传输协议和编码的封装。
  24.  
  1. 关于OperationContract WebGet 特性的说明
    这两个特性主要是给Operation的描述添加了一些元数据信息,给一个Operation添加了这两个特性以后,该Operation仍然可以通过SOAP消息的方式来访问。

WCF中的REST是什么的更多相关文章

  1. WCF中,通过C#代码或App.config配置文件创建ServiceHost类

    C# static void Main(string[] args) { //创建宿主的基地址 Uri baseAddress = new Uri("http://localhost:808 ...

  2. WCF学习之旅—WCF中传统的异常处理(十六)

    WCF中的异常处理 在软件开发过程中,不可能没有异常的出现,所以在开发过程中,对不可预知的异常进行解决时,异常处理显得尤为重要.对于一般的.NET系统来说,我们简单地借助try/catch可以很容易地 ...

  3. 在Wcf中应用ProtoBuf替代默认的序列化器

    Google的ProtoBuf序列化器性能的牛逼已经有目共睹了,可以把它应用到Socket通讯,队列,Wcf中,身为dotnet程序员一边期待着不久后Grpc对dotnet core的支持更期待着Wc ...

  4. ajax调用本地wcf中的post和get

    我们可以通过jQuery调用本地或者远程的wcf服务,本文讲解的是对本地wcf服务的post和get调用方式. post和get到底有什么区别呢?此处不作详述. 但是,post对请求的数据格式更为严格 ...

  5. 在 WCF 中使用高效的 BinaryFormatter 序列化

    本文将定义一个 WCF 终结点行为扩展,以在 WCF 中使用更高效的 BinaryFormatter 进行二进制序列化,并实现对是否使用传统二进制序列化功能的可配置. 介绍 实现步骤 使用方法 效果 ...

  6. 【WCF】WCF中的InstanceContext与ConcurrencyMode【转】

    一.前言 最近忙于公司的在线升级项目,一个人要负责公司四大产品的在线升级,这四个产品是在Revit中以插件形式存在的,目前基于WCF来实现.等客户总量突破5万了,再重新用socket实现. 由于有服务 ...

  7. WCF初探-28:WCF中的并发

    理解WCF中的并发机制 在对WCF并发机制进行理解时,必须对WCF初探-27:WCF中的实例化进行理解,因为WCF中的并发特点是伴随着服务实例上下文实现的.WCF的实例上下文模型可以通过Instanc ...

  8. WCF初探-27:WCF中的实例化

    理解WCF中的实例化机制 “实例化”是指对用户定义的服务对象以及与其相关的 InstanceContext 对象的生存期的控制.也就是说我们的客户端程序在调用服务端方法时,需要实例化一个服务端代理类对 ...

  9. WCF初探-26:WCF中的会话

    理解WCF中的会话机制 在WCF应用程序中,会话将一组消息相互关联,从而形成对话.会话”是在两个终结点之间发送的所有消息的一种相互关系.当某个服务协定指定它需要会话时,该协定会指定所有调用(即,支持调 ...

  10. WCF初探-25:WCF中使用XmlSerializer类

    前言 在上一篇WCF序列化和反序列化中,文章介绍了WCF序列化和反序列化的机制,虽然WCF针对序列化提供了默认的DataContractSerializer序列化引擎,但是WCF还支持其他的序列化引擎 ...

随机推荐

  1. 论参数self

    此篇文章仅适用于py3.在py2中,a.fuc(x)中的参数x必须是 类a的实例对象,而py3则可以是任意对象.参考绑定方法和非绑定方法 当一个对象添加了一个方法,并且此方法的第一个参数为self,或 ...

  2. 工作目录与os.getcwd()

    假设某程序在/root/a/aa.py,在shell,当前pwd为/root,输入./a/aa.py运行py程序,则爱程序的工作目录是/root.而不是程序所在文件夹,os.getcwd()就是查看工 ...

  3. 底板芯片组与内存映射(Motherboard Chipsets and the Memory Map) 【转】

    转自:http://blog.chinaunix.net/uid-25909619-id-4194650.html 底板芯片组与内存映射 我打算写一些关于计算机内部构造(computer intern ...

  4. tar命令的实用详解(C参数和排除文件 --exclude)

    一.tar:从压缩包中解压出指定文件 [root@d176 test]# tar ztf nrpe-2.12.tar.gz |grep srcnrpe-2.12/src/nrpe-2.12/src/. ...

  5. make distclean

    清空bin目录make dirclean 清空所有相关的东西,包括下载的软件包,配置文件,feeds内容等make distclean 这个命令会删除feeds目录及其下面的所有的文件,直接结果就是运 ...

  6. poj1996

    多项式计算,有点像母函数,注意最后要判断最高次项是否为0. #include <cstdio> #include <cstring> using namespace std; ...

  7. poj2054

    题意:给定一棵树,每个节点有一个权值,现要求给这些节点进行排列,设排列后的节点顺序为v1~vn,它们的权值是w1~wn,那么我们要求一种排列使得w1*1+w2*2+...+wn*n最小.还有一个限制就 ...

  8. 10 Go 1.10 Release Notes

    Go 1.10 Release Notes Introduction to Go 1.10 Changes to the language Ports Tools Default GOROOT &am ...

  9. Java 基本语法---Java数组

    Java 基本语法---Java数组 0. 概述 数组:相同类型的数据 按照顺序 组成的一种 引用数据类型 . 数据类型:基本数据类型 + 引用数据类型: 引用数据类型:类 + 接口 + 数组 : 一 ...

  10. Android Service总结06 之AIDL

    Android Service总结06 之AIDL 版本 版本说明 发布时间 发布人 V1.0 初始版本 2013-04-03 Skywang           1 AIDL介绍 AIDL,即And ...