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] publicView Codeinterface 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; } } }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 resultView 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:guidView 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 = KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1View 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].ValueView 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‘.")
Python之Suds庫調用WCF實現復雜參數序列化