• Python之Suds库调用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'.")
  • 相关阅读:
    Something I know about WebDynpro
    Details about support package implementation
    CRM Middleware Performance Topics
    Way to configure the logon navigaion layouts via Business Roles in CRM
    DOM 常用节点类型和方法
    第一届 xdef 会议日程
    去除百度音乐盒广告的chrome插件 持续更新
    从人人网抓取高校数据信息,包括,省份 高校 院系 (提供最终SQL文件下载)
    PHP 与 JSON
    解决HTTPS 发送请求走socket问题
  • 原文地址:https://www.cnblogs.com/5ishare/p/8438642.html
Copyright © 2020-2023  润新知