Python之Suds库调用WCF实现复杂参数序列化
Posted 小崔笔记本
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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; } } }
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; } } }
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>
3.启动服务
static void Main(string[] args) { using (ServiceHost host = new ServiceHost(typeof(WcfServiceDemo.ServiceDemo))) { host.Open(); Console.WriteLine("服务已开启"); Console.Read(); } }
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
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
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
具体实现
# -*- 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
三、遇到的问题
在上面配置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>
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]) ArrayOfKeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D以上是关于Python之Suds库调用WCF实现复杂参数序列化的主要内容,如果未能解决你的问题,请参考以下文章
python用suds 调用webservice方法的时候报错。
如何使用python完成对WebService服务的调用?suds-py3插件安利一下!
Soap 请求在 Soapui 中运行良好,但在 python 中运行良好 - ZEEP - SUDS