Setting Up the WCF Service
Defining Service Contracts
The IServiceDemo.cs file defines the service contract along with five operation contracts that handle different parameter types for testing purposes. Two custom data contracts are also defined for data exchange.
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
{
[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; }
}
}
The service implementation in ServiceDemo.svc.cs returns the input parameters to verify correct serialization:
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
{
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;
}
}
}
Configuring the Host
A console application serves as the service host for easy deployment during testing. The App.config file configures the WCF service with basicHttpBinding:
<?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">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<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>
Starting the Service
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(typeof(WcfServiceDemo.ServiceDemo)))
{
host.Open();
Console.WriteLine("Service started successfully");
Console.Read();
}
}
Common Isue: If you encounter "HTTP could not register URL http://+:8001/ServiceDemo/" error, run Visual Studio as Administrator.
Working with Suds Client
Discovering the WSDL Structure
First, instantiate the suds client and print its contents to understand the available methods and parameter types:
# -*- 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)
The output reveals the available methods and type definitions:
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
...
Handling Complex Parameter Serialization
Primitive types can be passed directly, but complex types require careful construction. The key is to build objects from the leaf level upward. Use client.factory.create() to instantiate parameters, starting with the innermost types.
For deeply nested structures like GetDicDicData, explore the type hierarchy first:
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'))
kv_int = client.factory.create('ns2:KeyValueOfstringint')
kv_int.Key = 'test_key'
kv_int.Value = 1
arr_kv_int = client.factory.create('ns2:ArrayOfKeyValueOfstringint')
arr_kv_int.KeyValueOfstringint = [kv_int]
arr_arr_kv_int = client.factory.create('ns2:ArrayOfArrayOfKeyValueOfstringint')
arr_arr_kv_int.ArrayOfKeyValueOfstringint = [arr_kv_int]
kv_arr = client.factory.create('ns2:KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1')
kv_arr.Key = 'outer_key'
kv_arr.Value = arr_arr_kv_int
outer_arr = client.factory.create('ns2:ArrayOfKeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1')
outer_arr.KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1 = kv_arr
Complete implementation showing all parameter types:
# -*- 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')
# Simple type
result = client.service.GetSimpleData('123')
print(result)
# Custom model type
menu_item = client.factory.create('ns0:ItemMenu')
menu_item.Name = 'TestName'
menu_item.Value = 'TestValue'
menu_array = client.factory.create('ns0:ArrayOfItemMenu')
menu_array.ItemMenu = [menu_item, menu_item]
item = client.factory.create('ns0:Item')
item.ItemMenus = menu_array
result = client.service.GetModelData(item)
print(result)
print(result.ItemMenus.ItemMenu[0].Name)
print(result.ItemMenus.ItemMenu[0].Value)
# List of custom models
items_array = client.factory.create('ns0:ArrayOfItem')
items_array.Item = [item, item]
result = client.service.GetListData(items_array)
print(result)
print(result.Item[0].ItemMenus.ItemMenu[0].Name)
# Dictionary type
kv_string = client.factory.create('ns2:KeyValueOfstringstring')
kv_string.Key = '01'
kv_string.Value = 'test_value'
kv_array = client.factory.create('ns2:ArrayOfKeyValueOfstringstring')
kv_array.KeyValueOfstringstring = [kv_string]
result = client.service.GetDicData(kv_array)
print(result.KeyValueOfstringstring[0].Key)
print(result.KeyValueOfstringstring[0].Value)
# Nested dictionary (leaf-level construction)
kv_inner = client.factory.create('ns2:KeyValueOfstringint')
kv_inner.Key = 'inner_key'
kv_inner.Value = 42
arr_inner = client.factory.create('ns2:ArrayOfKeyValueOfstringint')
arr_inner.KeyValueOfstringint = [kv_inner]
arr_arr_inner = client.factory.create('ns2:ArrayOfArrayOfKeyValueOfstringint')
arr_arr_inner.ArrayOfKeyValueOfstringint = [arr_inner]
kv_outer = client.factory.create('ns2:KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1')
kv_outer.Key = 'outer_key'
kv_outer.Value = arr_arr_inner
outer_container = client.factory.create('ns2:ArrayOfKeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1')
outer_container.KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1 = kv_outer
result = client.service.GetDicDicData(outer_container)
print(result.KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1[0].Key)
print(result.KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1[0].Value)
Troubleshooting wsHttpBinding
When configuring the endpoint with wsHttpBinding, suds may fail with this error:
<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'.")
Suds does not support wsHttpBinding. Use zeep instead for this binding type.
Using Zeep Client Library
WCF Service Configuration for Zeep
When using zeep, configure wsHttpBinding with security mode set to None:
<?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:8021/ServiceDemo/"></add>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceDemoBehavior">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<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>
Parameter Serialization with Zeep
Zeep provides a cleaner API for creating complex types using client.get_type(). Dictionary parameters can use dictionary literal syntax:
# -*- 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)
# Basic type
print(client.service.GetSimpleData('abc'))
# Custom model type
menu_type = client.get_type('ns2:ItemMenu')
menu_instance = menu_type(Name='user_name', Value='user_value')
arr_menu_type = client.get_type('ns2:ArrayOfItemMenu')
menu_list = arr_menu_type(ItemMenu=[menu_instance, menu_instance])
item_type = client.get_type('ns2:Item')
item_instance = item_type(ItemMenus=menu_list)
print(client.service.GetModelData(item_instance))
# List of custom models
arr_item_type = client.get_type('ns2:ArrayOfItem')
items_collection = arr_item_type(Item=[item_instance, item_instance])
print(client.service.GetListData(items_collection))
# Dictionary type - using dict literals
kv_str_type = client.get_type('ns3:ArrayOfKeyValueOfstringstring')
dict_data = kv_str_type(KeyValueOfstringstring=[{'Key': 'a', 'Value': 'aaa'}, {'Key': 'b', 'Value': 'bbb'}])
print(dict_data)
# Nested dictionary types
kv_int_type = client.get_type('ns3:ArrayOfKeyValueOfstringint')
inner_dict = kv_int_type(KeyValueOfstringint=[{'Key': 'a', 'Value': 1}, {'Key': 'b', 'Value': 2}])
arr_arr_type = client.get_type('ns3:ArrayOfArrayOfKeyValueOfstringint')
nested_arr = arr_arr_type(ArrayOfKeyValueOfstringint=[inner_dict])
outer_dict_type = client.get_type('ns3:ArrayOfKeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1')
complex_dict = outer_dict_type(KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1={'Key': 'c', 'Value': nested_arr})
result = client.service.GetDicDicData(complex_dict)
print(result)
print(result[0].Value.ArrayOfKeyValueOfstringint[0].KeyValueOfstringint[0].Key)
To view detailed WSDL information:
print(client.wsdl.dump())