DCSIMG
Flat WSDL for WCF services - Manu Cohen-Yashar's Blog

Manu Cohen-Yashar's Blog

Flat WSDL for WCF services

In my last post I motioned that working with more than one XML namespace will cause problems when a flat WSDL is a requirement. I was asked many questions about flat WSDL so I would like to explain here about creating flat WSDL for WCF services.

As you have all seen the WSDL created by WCF is very different than the WSDL asmx web services used to expose. The content is similar but the structure is different. WCF uses references to other links (for example the XSDs are found using the reference : <xsd:import schemaLocation="http://localhost:8080/?xsd=xsd0 ...

There are systems that fail to read this WSDL and that create an interoperability problem.

To make WCF expose the WSDL in a asmx like form or in other words in a flat manner – no references etc , a special endpoint behavior should be used.

Here is the code of the FlatWSDL endpointBehavior:

namespace FlatWSDL

{

    public class FlatWsdlBefavior : BehaviorExtensionElement,

        IWsdlExportExtension, IEndpointBehavior

    {

        public void ExportContract(WsdlExporter exporter,

            WsdlContractConversionContext context)

        {

        }

 

        public void ExportEndpoint(WsdlExporter exporter,

            WsdlEndpointConversionContext context)

        {

            XmlSchemaSet schemaSet = exporter.GeneratedXmlSchemas;

 

            foreach (System.Web.Services.Description.ServiceDescription wsdl

                in exporter.GeneratedWsdlDocuments)

            {

                List<XmlSchema> importsList =

                                new List<XmlSchema>();

 

                foreach (XmlSchema schema in wsdl.Types.Schemas)

                {

                    AddImportedSchemas(schema,

                                       schemaSet, importsList);

                }

 

                wsdl.Types.Schemas.Clear();

 

                foreach (XmlSchema schema in importsList)

                {

                    RemoveXsdImports(schema);

                    wsdl.Types.Schemas.Add(schema);

                }

            }

        }

 

        private void AddImportedSchemas(XmlSchema schema,

            XmlSchemaSet schemaSet, List<XmlSchema> importsList)

        {

            foreach (XmlSchemaImport import in schema.Includes)

            {

                ICollection realSchemas =

                    schemaSet.Schemas(import.Namespace);

 

                foreach (XmlSchema ixsd in realSchemas)

                {

                    if (!importsList.Contains(ixsd))

                    {

                        importsList.Add(ixsd);

                        AddImportedSchemas(ixsd,

                            schemaSet,

                            importsList);

                    }

                }

            }

        }

 

        private void RemoveXsdImports(XmlSchema schema)

        {

            for (int i = 0; i < schema.Includes.Count; i++)

            {

                if (schema.Includes[i] is XmlSchemaImport)

                    schema.Includes.RemoveAt(i--);

            }

        }

 

        public void AddBindingParameters(ServiceEndpoint endpoint,

            BindingParameterCollection bindingParameters)

        {

        }

 

        public void ApplyClientBehavior(ServiceEndpoint endpoint,

            ClientRuntime clientRuntime)

        {

        }

 

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint,

            EndpointDispatcher endpointDispatcher)

        {

        }

 

        public void Validate(ServiceEndpoint endpoint)

        {

        }

 

        //To enable using configuration extention to use this behavior

        public override Type BehaviorType

        {

            get { return this.GetType(); }

        }

 

        //To enable using configuration extention to use this behavior

        protected override object CreateBehavior()

        {

            return new FlatWsdlBefavior();

        }

    }

}

Here is the service I will use to demonstrate the behavior

 

[ServiceContract(Namespace="http://manu.com")]

    public interface ICalc

    {

        [OperationContract]

        int Add(int a, int b);

        [OperationContract]

        int Sub(int a, int b);

        [OperationContract]

        int Mul(int a, int b);

        [OperationContract]

        int Div(int a, int b);

    }

 

[ServiceBehavior(Namespace="http://manu.com")]

    public class Calc : ICalc

    {

        #region ICalc Members

 

        public int Add(int a, int b)

        {

            return a+b;

        }

 

        public int Sub(int a, int b)

        {

            return a - b;

        }

 

        public int Mul(int a, int b)

        {

            return a * b;

        }

 

        public int Div(int a, int b)

        {

            return a / b;

        }

 

        #endregion

    }

To use this behavior I will a wcf configuration extension.

This behavior derive from BehaviorExtensionElement So I can create an extension in my configuration file and use it in config:

 

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.serviceModel>

    <extensions>

       <behaviorExtensions>

          <add name="FlatWSDL" type="FlatWSDL.FlatWsdlBefavior, FlatWSDL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>

       </behaviorExtensions>

    </extensions>

    <behaviors>

      <serviceBehaviors>

        <behavior name="ShowMetadata">

          <serviceMetadata  httpGetEnabled="true"/>

        </behavior>

      </serviceBehaviors>

      <endpointBehaviors>

        <behavior name="ExposeFlatWSDL">

          <FlatWSDL/>

        </behavior>

      </endpointBehaviors>

    </behaviors>

 

    <services>

      <service name="FlatWSDL.Calc"

              behaviorConfiguration="ShowMetadata">

        <host>

          <baseAddresses>

            <add baseAddress="http://localhost:8080"/>

          </baseAddresses>

        </host>

        <endpoint address="Calc"

                  binding="basicHttpBinding"

                  contract="FlatWSDL.ICalc"

                  behaviorConfiguration="ExposeFlatWSDL">         

        </endpoint>

      </service>

    </services>

  </system.serviceModel>

</configuration>

Look at the the Extension element in the top of the config file. As you can see I have to use the full qualified name of the assembly. It just does not work otherwise. (known bug…)

Now I will open the host, load the service and look at its WSDL and here is the results:

<?xml version="1.0" encoding="utf-8" ?>
- <wsdl:definitions name="Calc" targetNamespace="http://manu.com" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:tns="http://manu.com" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:i0="http://tempuri.org/" xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:wsa10="http://www.w3.org/2005/08/addressing" xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex">
  <wsdl:import namespace="http://tempuri.org/" location="http://localhost:8080/?wsdl=wsdl0" />
- <wsdl:types>
- <xsd:schema elementFormDefault="qualified" targetNamespace="http://manu.com">
- <xsd:element name="Add">
- <xsd:complexType>
- <xsd:sequence>
  <xsd:element minOccurs="0" name="a" type="xsd:int" />
  <xsd:element minOccurs="0" name="b" type="xsd:int" />
  </xsd:sequence>
  </xsd:complexType>
  </xsd:element>
- <xsd:element name="AddResponse">
- <xsd:complexType>
- <xsd:sequence>
  <xsd:element minOccurs="0" name="AddResult" type="xsd:int" />
  </xsd:sequence>
  </xsd:complexType>
  </xsd:element>
- <xsd:element name="Sub">
- <xsd:complexType>
- <xsd:sequence>
  <xsd:element minOccurs="0" name="a" type="xsd:int" />
  <xsd:element minOccurs="0" name="b" type="xsd:int" />
  </xsd:sequence>
  </xsd:complexType>
  </xsd:element>
- <xsd:element name="SubResponse">
- <xsd:complexType>
- <xsd:sequence>
  <xsd:element minOccurs="0" name="SubResult" type="xsd:int" />
  </xsd:sequence>
  </xsd:complexType>
  </xsd:element>
- <xsd:element name="Mul">
- <xsd:complexType>
- <xsd:sequence>
  <xsd:element minOccurs="0" name="a" type="xsd:int" />
  <xsd:element minOccurs="0" name="b" type="xsd:int" />
  </xsd:sequence>
  </xsd:complexType>
  </xsd:element>
- <xsd:element name="MulResponse">
- <xsd:complexType>
- <xsd:sequence>
  <xsd:element minOccurs="0" name="MulResult" type="xsd:int" />
  </xsd:sequence>
  </xsd:complexType>
  </xsd:element>
- <xsd:element name="Div">
- <xsd:complexType>
- <xsd:sequence>
  <xsd:element minOccurs="0" name="a" type="xsd:int" />
  <xsd:element minOccurs="0" name="b" type="xsd:int" />
  </xsd:sequence>
  </xsd:complexType>
  </xsd:element>
- <xsd:element name="DivResponse">
- <xsd:complexType>
- <xsd:sequence>
  <xsd:element minOccurs="0" name="DivResult" type="xsd:int" />
  </xsd:sequence>
  </xsd:complexType>
  </xsd:element>
  </xsd:schema>
- <xs:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://schemas.microsoft.com/2003/10/Serialization/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://schemas.microsoft.com/2003/10/Serialization/">
  <xs:element name="anyType" nillable="true" type="xs:anyType" />
  <xs:element name="anyURI" nillable="true" type="xs:anyURI" />
  <xs:element name="base64Binary" nillable="true" type="xs:base64Binary" />
  <xs:element name="boolean" nillable="true" type="xs:boolean" />
  <xs:element name="byte" nillable="true" type="xs:byte" />
  <xs:element name="dateTime" nillable="true" type="xs:dateTime" />
  <xs:element name="decimal" nillable="true" type="xs:decimal" />
  <xs:element name="double" nillable="true" type="xs:double" />
  <xs:element name="float" nillable="true" type="xs:float" />
  <xs:element name="int" nillable="true" type="xs:int" />
  <xs:element name="long" nillable="true" type="xs:long" />
  <xs:element name="QName" nillable="true" type="xs:QName" />
  <xs:element name="short" nillable="true" type="xs:short" />
  <xs:element name="string" nillable="true" type="xs:string" />
  <xs:element name="unsignedByte" nillable="true" type="xs:unsignedByte" />
  <xs:element name="unsignedInt" nillable="true" type="xs:unsignedInt" />
  <xs:element name="unsignedLong" nillable="true" type="xs:unsignedLong" />
  <xs:element name="unsignedShort" nillable="true" type="xs:unsignedShort" />
  <xs:element name="char" nillable="true" type="tns:char" />
- <xs:simpleType name="char">
  <xs:restriction base="xs:int" />
  </xs:simpleType>
  <xs:element name="duration" nillable="true" type="tns:duration" />
- <xs:simpleType name="duration">
- <xs:restriction base="xs:duration">
  <xs:pattern value="\-?P(\d*D)?(T(\d*H)?(\d*M)?(\d*(\.\d*)?S)?)?" />
  <xs:minInclusive value="-P10675199DT2H48M5.4775808S" />
  <xs:maxInclusive value="P10675199DT2H48M5.4775807S" />
  </xs:restriction>
  </xs:simpleType>
  <xs:element name="guid" nillable="true" type="tns:guid" />
- <xs:simpleType name="guid">
- <xs:restriction base="xs:string">
  <xs:pattern value="[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12}" />
  </xs:restriction>
  </xs:simpleType>
  <xs:attribute name="FactoryType" type="xs:QName" />
  <xs:attribute name="Id" type="xs:ID" />
  <xs:attribute name="Ref" type="xs:IDREF" />
  </xs:schema>
  </wsdl:types>
- <wsdl:message name="ICalc_Add_InputMessage">
  <wsdl:part name="parameters" element="tns:Add" />
  </wsdl:message>
- <wsdl:message name="ICalc_Add_OutputMessage">
  <wsdl:part name="parameters" element="tns:AddResponse" />
  </wsdl:message>
- <wsdl:message name="ICalc_Sub_InputMessage">
  <wsdl:part name="parameters" element="tns:Sub" />
  </wsdl:message>
- <wsdl:message name="ICalc_Sub_OutputMessage">
  <wsdl:part name="parameters" element="tns:SubResponse" />
  </wsdl:message>
- <wsdl:message name="ICalc_Mul_InputMessage">
  <wsdl:part name="parameters" element="tns:Mul" />
  </wsdl:message>
- <wsdl:message name="ICalc_Mul_OutputMessage">
  <wsdl:part name="parameters" element="tns:MulResponse" />
  </wsdl:message>
- <wsdl:message name="ICalc_Div_InputMessage">
  <wsdl:part name="parameters" element="tns:Div" />
  </wsdl:message>
- <wsdl:message name="ICalc_Div_OutputMessage">
  <wsdl:part name="parameters" element="tns:DivResponse" />
  </wsdl:message>
- <wsdl:portType name="ICalc">
- <wsdl:operation name="Add">
  <wsdl:input wsaw:Action="http://manu.com/ICalc/Add" message="tns:ICalc_Add_InputMessage" />
  <wsdl:output wsaw:Action="http://manu.com/ICalc/AddResponse" message="tns:ICalc_Add_OutputMessage" />
  </wsdl:operation>
- <wsdl:operation name="Sub">
  <wsdl:input wsaw:Action="http://manu.com/ICalc/Sub" message="tns:ICalc_Sub_InputMessage" />
  <wsdl:output wsaw:Action="http://manu.com/ICalc/SubResponse" message="tns:ICalc_Sub_OutputMessage" />
  </wsdl:operation>
- <wsdl:operation name="Mul">
  <wsdl:input wsaw:Action="http://manu.com/ICalc/Mul" message="tns:ICalc_Mul_InputMessage" />
  <wsdl:output wsaw:Action="http://manu.com/ICalc/MulResponse" message="tns:ICalc_Mul_OutputMessage" />
  </wsdl:operation>
- <wsdl:operation name="Div">
  <wsdl:input wsaw:Action="http://manu.com/ICalc/Div" message="tns:ICalc_Div_InputMessage" />
  <wsdl:output wsaw:Action="http://manu.com/ICalc/DivResponse" message="tns:ICalc_Div_OutputMessage" />
  </wsdl:operation>
  </wsdl:portType>
- <wsdl:service name="Calc">
- <wsdl:port name="BasicHttpBinding_ICalc" binding="i0:BasicHttpBinding_ICalc">
  <soap:address location="http://localhost:8080/Calc" />
  </wsdl:port>
  </wsdl:service>
  </wsdl:definitions>

The WSDL is flat !!!!

One last note

The XML nampsaces on the service contract and the service implementation must match. The problem is that the service implementation support only one XML namespaces so overloading (see last post) and flat WSDL are not possible together.

Manu

Comments

No Comments

Leave a Comment

(required) 

(required) 

(optional)

(required) 


Enter the numbers above: