zoukankan      html  css  js  c++  java
  • WCF中的REST是什么

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

    SOAP示例

    <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" 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>

    Body部分省略
    <s:Body>........</s:Body>

    </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的定义

        [ServiceContract]
        public interface IService1
        { 
            [OperationContract]
            CompositeType GetDataUsingDataContract(CompositeType composite);
    
        }


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

    <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:

    <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,比如:

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



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

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

    <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特性需要修改一下,处理那些找不到调用信息的请求:

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

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

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

            public Message Resource(Message input)
            {
                HttpRequestMessageProperty httpRequestProperty = (HttpRequestMessageProperty)input.Properties[HttpRequestMessageProperty.Name];
                string query = httpRequestProperty.QueryString;
                string fileName = string.Format("{0}.xml", query);
                switch (httpRequestProperty.Method)
                {
                    case "GET":
                        Message message = null;
                        if (File.Exists(fileName))
                        {
                            XmlDocument document = new XmlDocument();
                            document.Load(fileName);
                            message = Message.CreateMessage(
                            MessageVersion.None,
                            "*",
                            new XmlNodeReader(document.DocumentElement));
                        }
                        else
                        {
                            message = Message.CreateMessage(
                            MessageVersion.None,
                            "*");
                        }
                        return message;
                    default:
                        return Message.CreateMessage(
                        MessageVersion.None, "*");
                }
            }

    相应的配置文件,不用多说了吧,既然采用的是REST,那么编码方式肯定就是文本,传输方式肯定就是Http。注意MessageVersion = None意味着咱们不需要对输入的数据的格式有要求
        <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>

    当然,这么写是有点小麻烦,WCF的设计者肯定会考虑到这点,帮我们采用URI模板方式封装好了,对比:

    原始方式:
    [OperationContract(Action = "*", ReplyAction = "*")]
    Message Resource(Message input);
    新的方式:
    [OperationContract]
    [WebGet(UriTemplate = “/{query}”, BodyStyle = WebMessageBodyStyle.Bare)]
    Message Resource(string query);

    这样我们在实现方法的时候就不用再自己手动调用 HttpRequestMessageProperty 类提取信息,也不再判断Method类型,因此重写后的Resource方法看起来也清爽了很多。
            public Message Resource(String query)
            {
                string fileName = string.Format("{0}.xml", query);
                        Message message = null;
                        if (File.Exists(fileName))
                        {
                            XmlDocument document = new XmlDocument();
                            document.Load(fileName);
                            message = Message.CreateMessage(
                            MessageVersion.None,
                            "*",
                            new XmlNodeReader(document.DocumentElement));
                        }
                        else
                        {
                            message = Message.CreateMessage(
                            MessageVersion.None,
                            "*");
                        }
                        return message;
            }

    同样的,系统给了我们一个webHttpBinding套餐,也是对上面传输协议和编码的封装。


    关于OperationContract 和 WebGet 特性的说明
    这两个特性主要是给Operation的描述添加了一些元数据信息,给一个Operation添加了这两个特性以后,该Operation仍然可以通过SOAP消息的方式来访问。


  • 相关阅读:
    北京燃气IC卡充值笔记
    随机分析、随机控制等科目在量化投资、计算金融方向有哪些应用?
    量化交易平台大全
    Doctor of Philosophy in Computational and Mathematical Engineering
    Institute for Computational and Mathematical Engineering
    Requirements for the Master of Science in Computational and Mathematical Engineering
    MSc in Mathematical and Computational Finance
    万字长文:详解多智能体强化学习的基础和应用
    数据处理思想和程序架构: 使用Mbedtls包中的SSL,和服务器进行网络加密通信
    31-STM32+W5500+AIR202/302基本控制篇-功能优化-W5500移植mbedtls库以SSL方式连接MQTT服务器(单向忽略认证)
  • 原文地址:https://www.cnblogs.com/lwzz/p/2334610.html
Copyright © 2011-2022 走看看