• Python之调用WCF实现复杂参数序列化


    今年主要做自动化测试技术支持工作,最近一直在做接口自动化这块,前些天在研究将web页面模拟http进行接口自动化,这周杭州那边想测试WCF服务,所以这两天一直在探索。遇到的第一个问题就是服务参数传参序列化的问题,怎么让python这边创建的对象能被WCF识别到。正好在大学的时候也学了WCF,不过一直都没用过,这次算是重温一下,用的都是一些WCF基础。

    一、WCF服务准备

    1.定义契约Contract

    这里IServiceDemo.cs定义了服务契约IServiceDemo,并定义了几个操作契约OperationContract,5个操作契约传的参数不同,用来做测试,同时自定义了两个数据契约DataContract.并在ServiceDemo.svc中实现了上面操作契约。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.ServiceModel.Web;
    using System.Text;
    
    namespace WcfServiceDemo
    {
        // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的接口名“IService1”。
        [ServiceContract]
        public interface IServiceDemo
        {
    
            [OperationContract]
            string GetSimpleData(string value);
    
            [OperationContract]
            List<Item> GetListData(List<Item> items);
    
            [OperationContract]
            Item GetModelData(Item item);
    
            [OperationContract]
            Dictionary<string,string> GetDicData(Dictionary<string,string> dic);
    
            [OperationContract]
            Dictionary<string, Dictionary<string,int>[]> GetDicDicData(Dictionary<string, Dictionary<string, int>[]> dic);
    
    
    
        }
        [DataContract]
        public class ItemMenu
        {
            [DataMember]
            public string Name { get; set; }
            [DataMember]
            public string Value { get; set; }
        }
        [DataContract]
        public class Item
        {
            [DataMember]
            public List<ItemMenu> ItemMenus { get; set; }
        }
    
    }
    View Code
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.ServiceModel.Web;
    using System.Text;
    
    namespace WcfServiceDemo
    {
        // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码、svc 和配置文件中的类名“Service1”。
        // 注意: 为了启动 WCF 测试客户端以测试此服务,请在解决方案资源管理器中选择 Service1.svc 或 Service1.svc.cs,然后开始调试。
        public class ServiceDemo : IServiceDemo
        {
    
            public string GetSimpleData(string value)
            {
                return value;
            }
            public List<Item> GetListData(List<Item> items)
            {
                return items;
            }
            public Item GetModelData(Item item)
            {
                return item;
            }
            public Dictionary<string, string> GetDicData(Dictionary<string, string> dic)
            {
                return dic;
            }
            public Dictionary<string, Dictionary<string, int>[]> GetDicDicData(Dictionary<string, Dictionary<string, int>[]> dic)
            {
                return dic;
            }
    
        }
    }
    View Code

    2.定义宿主

    WCF宿主可以有多种方式,这里用了控制台应用程序来作为宿主,主要是想着做demo,可以发给测试,用控制台不用像iis那样部署了。在控制台应用程序的App.config中配置wcf服务。

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.serviceModel>
        <services>
          <service name="WcfServiceDemo.ServiceDemo" behaviorConfiguration="ServiceDemoBehavior" >
            <endpoint address="" contract="WcfServiceDemo.IServiceDemo" binding="basicHttpBinding"></endpoint>
            <host>
              <baseAddresses>
                <add baseAddress="http://localhost:8001/ServiceDemo/"></add>
              </baseAddresses>
            </host>
          </service>
        </services>
        <behaviors>
          <serviceBehaviors>
            <behavior name="ServiceDemoBehavior">
              <!-- 为避免泄漏元数据信息,请在部署前将以下值设置为 false -->
              <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
              <!-- 要接收故障异常详细信息以进行调试,请将以下值设置为 true。在部署前设置为 false 以避免泄漏异常信息 -->
              <serviceDebug includeExceptionDetailInFaults="false"/>
            </behavior>
          </serviceBehaviors>
        </behaviors>
        <protocolMapping>
          <add binding="basicHttpsBinding" scheme="https" />
        </protocolMapping>
        <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
      </system.serviceModel>
        <startup> 
            <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
        </startup>
    </configuration>
    View Code

    3.启动服务

            static void Main(string[] args)
            {
                using (ServiceHost host = new ServiceHost(typeof(WcfServiceDemo.ServiceDemo)))
                {
                    host.Open();
                    Console.WriteLine("服务已开启");
                    Console.Read();
                }
            }
    View Code

    4.出现的问题

    在启动服务的时候,报了:HTTP 无法注册 URL http://+:8001/ServiceDemo/。进程不具有此命名空间的访问权限的错误。解决方法是VS2015用管理员打开就好了。

    二.suds.client的使用

    1.了解WCF

    要调用WCF,首先得知道服务中有哪些参数,每个参数具体是什么类型。可以使用sud.client实例化client,然后打印出来看服务里面的内容。

    # -*- coding: utf-8 -*-
    import sys
    reload(sys)
    sys.setdefaultencoding('utf-8')
    
    from suds.client import Client
    
    if __name__ == '__main__':
    
        client=Client('http://localhost:8001/ServiceDemo/?singleWsdl')
        print client
        # -----------------简单类型---------------------------
        result= client.service.GetSimpleData('123')
        print result
    View Code
    Service ( ServiceDemo ) tns="http://tempuri.org/"
       Prefixes (4)
          ns0 = "http://schemas.datacontract.org/2004/07/WcfServiceDemo"
          ns1 = "http://schemas.microsoft.com/2003/10/Serialization/"
          ns2 = "http://schemas.microsoft.com/2003/10/Serialization/Arrays"
          ns3 = "http://tempuri.org/"
       Ports (1):
          (BasicHttpBinding_IServiceDemo)
             Methods (5):
                GetDicData(ns2:ArrayOfKeyValueOfstringstring dic, )
                GetDicDicData(ns2:ArrayOfKeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1 dic, )
                GetListData(ns0:ArrayOfItem items, )
                GetModelData(ns0:Item item, )
                GetSimpleData(xs:string value, )
             Types (11):
                ns2:ArrayOfArrayOfKeyValueOfstringint
                ns0:ArrayOfItem
                ns0:ArrayOfItemMenu
                ns2:ArrayOfKeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1
                ns2:ArrayOfKeyValueOfstringint
                ns2:ArrayOfKeyValueOfstringstring
                ns0:Item
                ns0:ItemMenu
                ns1:char
                ns1:duration
                ns1:guid
    View Code

    2.参数序列化

    对于基础类型的参数可以直接传参,但复杂类型参数就比较麻烦了,怎么样在python定义的参数能在wcf服务端识别出来,也就是序列化反序列化的问题,例如GetDicDicData方法中要传递ns2:ArrayOfKeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1类型的参数,在python中怎么定义呢,这个类型里面包含哪些属性,怎么实例化这个参数,可以使用client.factory.create('参数类型名')来创建,有时类型下面还有子类,所以在传参数时要弄清楚对象里面子类的数据类型,从根到叶子,而在实例化参数时需要从叶子到根来组装成对象。还有获取结果后获取解析的问题,这个把结果打印出来后可以一层一层的获取值。也可以调用last_received()方法,返回的是xml,然后用xpath解析。

        print client.factory.create('ns2:ArrayOfKeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1')
        print client.factory.create('ns2:KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1')
        print client.factory.create('ns2:ArrayOfArrayOfKeyValueOfstringint')
        print client.factory.create('ns2:ArrayOfKeyValueOfstringint')
        print client.factory.create('ns2:KeyValueOfstringint')
        KeyValueOfstringint=client.factory.create('ns2:KeyValueOfstringint')
        KeyValueOfstringint.Key='cyw'
        KeyValueOfstringint.Value = 1
        ArrayOfKeyValueOfstringint=client.factory.create('ns2:ArrayOfKeyValueOfstringint')
        ArrayOfKeyValueOfstringint.KeyValueOfstringint=[KeyValueOfstringint]
        ArrayOfArrayOfKeyValueOfstringint=client.factory.create('ns2:ArrayOfArrayOfKeyValueOfstringint')
        ArrayOfArrayOfKeyValueOfstringint.ArrayOfKeyValueOfstringint=[ArrayOfKeyValueOfstringint]
        KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1 = client.factory.create('ns2:KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1')
        KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1.Key = 'cuiyw'
        KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1.Value = ArrayOfArrayOfKeyValueOfstringint
        ArrayOfKeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1 = client.factory.create('ns2:ArrayOfKeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1')
        ArrayOfKeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1.KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1 = KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1
    View Code

    具体实现

    # -*- coding: utf-8 -*-
    import sys
    reload(sys)
    sys.setdefaultencoding('utf-8')
    
    from suds.client import Client
    
    if __name__ == '__main__':
    
        client=Client('http://localhost:8001/ServiceDemo/?singleWsdl')
        print client
        # -----------------简单类型---------------------------
        result= client.service.GetSimpleData('123')
        print result
    
        # -----------------自定义类---------------------------
        print client.factory.create('ns0:Item')
        print client.factory.create('ns0:ArrayOfItemMenu')
        print client.factory.create('ns0:ItemMenu')
        ItemMenu=client.factory.create('ns0:ItemMenu')
        ItemMenu.Name='Cyw'
        ItemMenu.Value = 'Cuiyw'
        ArrayOfItemMenu= client.factory.create('ns0:ArrayOfItemMenu')
        ArrayOfItemMenu.ItemMenu=[ItemMenu,ItemMenu]
        Item=client.factory.create('ns0:Item')
        Item.ItemMenus=ArrayOfItemMenu
        result= client.service.GetModelData(Item)
        print result
        print result.ItemMenus.ItemMenu[0].Name
        print result.ItemMenus.ItemMenu[0].Value
    
        # -----------------自定义类列表---------------------------
        print client.factory.create('ns0:ArrayOfItem')
        ArrayOfItem =client.factory.create('ns0:ArrayOfItem')
        ArrayOfItem.Item=[Item,Item]
        result= client.service.GetListData(ArrayOfItem)
        print result
        print result.Item[0].ItemMenus.ItemMenu[0].Name
        # -----------------字典类型---------------------------
        print client.factory.create('ns2:ArrayOfKeyValueOfstringstring')
        print client.factory.create('ns2:KeyValueOfstringstring')
        KeyValueOfstringstring= client.factory.create('ns2:KeyValueOfstringstring')
        KeyValueOfstringstring.Key='01'
        KeyValueOfstringstring.Value = 'cyw'
        ArrayOfKeyValueOfstringstring=client.factory.create('ns2:ArrayOfKeyValueOfstringstring')
        ArrayOfKeyValueOfstringstring.KeyValueOfstringstring=[KeyValueOfstringstring]
        result= client.service.GetDicData(ArrayOfKeyValueOfstringstring)
        print result.KeyValueOfstringstring[0].Key
        print result.KeyValueOfstringstring[0].Value
        # print client.last_received()
    
        # -----------------字典嵌套---------------------------
        print client.factory.create('ns2:ArrayOfKeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1')
        print client.factory.create('ns2:KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1')
        print client.factory.create('ns2:ArrayOfArrayOfKeyValueOfstringint')
        print client.factory.create('ns2:ArrayOfKeyValueOfstringint')
        print client.factory.create('ns2:KeyValueOfstringint')
        KeyValueOfstringint=client.factory.create('ns2:KeyValueOfstringint')
        KeyValueOfstringint.Key='cyw'
        KeyValueOfstringint.Value = 1
        ArrayOfKeyValueOfstringint=client.factory.create('ns2:ArrayOfKeyValueOfstringint')
        ArrayOfKeyValueOfstringint.KeyValueOfstringint=[KeyValueOfstringint]
        ArrayOfArrayOfKeyValueOfstringint=client.factory.create('ns2:ArrayOfArrayOfKeyValueOfstringint')
        ArrayOfArrayOfKeyValueOfstringint.ArrayOfKeyValueOfstringint=[ArrayOfKeyValueOfstringint]
        KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1 = client.factory.create('ns2:KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1')
        KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1.Key = 'cuiyw'
        KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1.Value = ArrayOfArrayOfKeyValueOfstringint
        ArrayOfKeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1 = client.factory.create('ns2:ArrayOfKeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1')
        ArrayOfKeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1.KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1 = KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1
        result= client.service.GetDicDicData(ArrayOfKeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1)
        print result.KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1[0].Key
        print result.KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1[0].Value
    View Code

    三、遇到的问题

    在上面配置WCF服务时我把终结点配置的绑定配置成wsHttpBinding,导致在python调用时出现下面的错误。当启动新实例启动服务时是可以的,但使用宿主就不行,昨天没找到解决方法,今天把昨天写的在自己电脑上重现了下还是出现这个问题,找了半天没想到还真解决了。

    <endpoint address="" contract="WcfServiceDemo.IServiceDemo" binding="wsHttpBinding"></endpoint>
    Exception: (415, u"Cannot process the message because the content type 'text/xml; charset=utf-8' was not the expected type 'application/soap+xml; charset=utf-8'.")

     四、zeep client库的使用

    昨天查Suds不支持wsHttpBinding,今天就尝试用zeep库来尝试。

    1、WCF服务配置

    首先是配置wsHttpBinding,使用zeep时需要wsHttpBinding配置<security mode="None">。其他与上一博客使用Suds序列化反序列化一样。

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.serviceModel>
         <bindings>
            <wsHttpBinding>
              <binding name="WSHttpBinding_IWCFService" >
              <security mode="None">
              </security>
              </binding>
            </wsHttpBinding>
          
        </bindings>
        <services>
          <service name="WcfServiceDemo.ServiceDemo" behaviorConfiguration="ServiceDemoBehavior" >
            <endpoint address="" contract="WcfServiceDemo.IServiceDemo" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IWCFService"></endpoint>
            <host>
              <baseAddresses>
                <add baseAddress="http://localhost:8001/ServiceDemo/"></add>
              </baseAddresses>
            </host>
          </service>
        </services>
        <behaviors>
          <serviceBehaviors>
            <behavior name="ServiceDemoBehavior">
              <!-- 为避免泄漏元数据信息,请在部署前将以下值设置为 false -->
              <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
              <!-- 要接收故障异常详细信息以进行调试,请将以下值设置为 true。在部署前设置为 false 以避免泄漏异常信息 -->
              <serviceDebug includeExceptionDetailInFaults="false"/>
            </behavior>
          </serviceBehaviors>
        </behaviors>
        <protocolMapping>
          <add binding="basicHttpsBinding" scheme="https" />
        </protocolMapping>
        <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
      </system.serviceModel>
        <startup> 
            <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
        </startup>
    </configuration>
    View Code

    2、zeep client的使用

    参数序列化反序列化,这里传入参数和返回值一样,就是为了验证传入参数正确与否。

    # -*- coding: utf-8 -*-
    import sys
    reload(sys)
    sys.setdefaultencoding('utf-8')
    from zeep import Client
    
    if __name__ == '__main__':
        client = Client('http://localhost:8021/ServiceDemo/?singleWsdl')
        print client
        print client.namespaces
        #-------------------基础数据类型---------------
        print client.service.GetSimpleData('abc')
        # -------------------自定义Model类型---------------
        ItemMenuType = client.get_type('ns2:ItemMenu')
        itemMenu= ItemMenuType(Name='cyw',Value='abc')
        ArrayOfItemMenuType = client.get_type('ns2:ArrayOfItemMenu')
        print ArrayOfItemMenuType
        itemMenus=ArrayOfItemMenuType(ItemMenu=[itemMenu,itemMenu])
        print itemMenus
        ItemType= client.get_type('ns2:Item')
        print ItemType
        item=ItemType(ItemMenus=itemMenus)
        print item
        print client.service.GetModelData(item)
        # -------------------自定义Model List类型---------------
        ArrayOfItem = client.get_type('ns2:ArrayOfItem')
        items= ArrayOfItem(Item=[item,item])
        print client.service.GetListData(items)
        # -------------------字典类型---------------
        ArrayOfKeyValueOfstringstringType = client.get_type('ns3:ArrayOfKeyValueOfstringstring')
        print ArrayOfKeyValueOfstringstringType
        dic=ArrayOfKeyValueOfstringstringType(KeyValueOfstringstring=[{'Key':'a','Value':'aaa'},{'Key':'b','Value':'bbb'}])
        print dic
        # -------------------字典嵌套类型---------------
        ArrayOfKeyValueOfstringintType = client.get_type('ns3:ArrayOfKeyValueOfstringint')
        print ArrayOfKeyValueOfstringintType
        dic=ArrayOfKeyValueOfstringintType(KeyValueOfstringint=[{'Key':'a','Value':1},{'Key':'b','Value':2}])
        print dic
        ArrayOfArrayOfKeyValueOfstringintType = client.get_type('ns3:ArrayOfArrayOfKeyValueOfstringint')
        arrdic= ArrayOfArrayOfKeyValueOfstringintType(ArrayOfKeyValueOfstringint=[dic])
        ArrayOfKeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1Type = client.get_type('ns3:ArrayOfKeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1')
        dicdic= ArrayOfKeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1Type(KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1={'Key':'c','Value':arrdic})
        result= client.service.GetDicDicData(dicdic)
        print result
        print result[0].Value.ArrayOfKeyValueOfstringint[0].KeyValueOfstringint[0].Key
    View Code

     一直没找到怎么打印出wsdl的详细信息,今天算是找到了.

    print client.wsdl.dump()
    View Code
  • 相关阅读:
    典型分布式系统分析:MapReduce
    linux下如何查看自己都装了什么服务
    docker
    linux的命令操作
    IDEA去除掉虚线,波浪线,和下划线实线的方法
    在linux下安装配置rabbitMQ详细教程
    在linux下安装配置rabbitMQ详细教程
    Shell特殊变量:Shell $0, $#, $*, $@, $?, $$和命令行参数
    shell之startup
    shell脚本特殊变量($0、$1、$2、 $?、 $# 、$@、 $*)
  • 原文地址:https://www.cnblogs.com/5ishare/p/8439020.html
Copyright © 2020-2023  润新知