1. 程式人生 > >Python之Suds庫調用WCF實現復雜參數序列化

Python之Suds庫調用WCF實現復雜參數序列化

rtu keyvalue microsoft ets tex sse exceptio https aps

今年主要做自動化測技術支持工作,最近一直在做接口自動化這塊,前些天在研究將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‘.")

Python之Suds庫調用WCF實現復雜參數序列化