WCF: Concept of FLAT WSDL

 

Ask:

Java Client (Non.Net client) unable to consume the WCF service WSDL document.

 

Reason:

After collecting fiddler traces from client side, I see that there is a problem while trying to get the XSD downloaded.

WCF expose XSD via external location and sometime Java client may not be able to access them.

 

Fiddler Traces extract:

First request from svcutil.exe

GET https://XYZ.com.com/MyService/MyServiceFile.svc?wsdl
HTTP/1.1

Response, we do get the WSDL doc:

========

HTTP/1.1 200 OK

Content-Type: text/xml; charset=UTF-8

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

<wsdl:definitions
name="MyServiceFile" targetNamespace="tempuri.org/"
xmlns:wsdl="schemas.xmlsoap.org/wsdl/"
xmlns:wsx="schemas.xmlsoap.org/ws/2004/09/mex"
xmlns:wsa10="www.w3.org/2005/08/addressing"
xmlns:tns="tempuri.org/" xmlns:soap12="schemas.xmlsoap.org/wsdl/soap12/"
xmlns:wsu="docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wsp="schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wsap="schemas.xmlsoap.org/ws/2004/08/addressing/policy"
xmlns:msc="schemas.microsoft.com/ws/2005/12/wsdl/contract"
xmlns:wsa="schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:wsam="www.w3.org/2007/05/addressing/metadata"
xmlns:wsaw="www.w3.org/2006/05/addressing/wsdl"
xmlns:soap="schemas.xmlsoap.org/wsdl/soap/"
xmlns:xsd="www.w3.org/2001/XMLSchema"
xmlns:soapenc="schemas.xmlsoap.org/soap/encoding/">

 <wsp:Policy
wsu:Id="MyRoutingEndpoint_policy"><wsp:ExactlyOne><wsp:All><sp:TransportBinding
xmlns:sp="schemas.xmlsoap.org/ws/2005/07/securitypolicy"><wsp:Policy><sp:TransportToken><wsp:Policy><sp:HttpsToken
RequireClientCertificate="false"/></wsp:Policy></sp:TransportToken><sp:AlgorithmSuite><wsp:Policy><sp:Basic256/></wsp:Policy></sp:AlgorithmSuite><sp:Layout><wsp:Policy><sp:Strict/></wsp:Policy></sp:Layout></wsp:Policy></sp:TransportBinding></wsp:All></wsp:ExactlyOne>

</wsp:Policy>

 <wsdl:types>

<xsd:schema
targetNamespace="tempuri.org/Imports">

<xsd:import
schemaLocation="XYZ.com.com/MyService/MyServiceFile.svc?xsd=xsd0"
namespace="tempuri.org/"/>

<xsd:import
schemaLocation="XYZ.com.com/MyService/MyServiceFile.svc?xsd=xsd1"
namespace="schemas.microsoft.com/2003/10/Serialization/"/>

<xsd:import
schemaLocation="XYZ.com.com/MyService/MyServiceFile.svc?xsd=xsd2"
namespace="schemas.microsoft.com/2003/10/Serialization/Arrays"/>

<xsd:import
schemaLocation="XYZ.com.com/MyService/MyServiceFile.svc?xsd=xsd3"
namespace="schemas.datacontract.org/2004/07/McAfeePortal.MyService"/>

<xsd:import
schemaLocation="XYZ.com.com/MyService/MyServiceFile.svc?xsd=xsd4"
namespace="schemas.datacontract.org/2004/07/McAfeePortal.PurchaseMailService"/>

</xsd:schema></wsdl:types><wsdl:message

 

Second: Because of wrong Schema location, we end up in sending the request to wrong address:

GET XYZ.com.com/MyService/MyServiceFile.svc?xsd=xsd0
HTTP/1.1

Because of this issue, my reference.cs file is blank.

 

Options:

Considering the requirement, the feature is added in latest and greatest WCF 4.5.

But what about client running on 4.0 ?

 

Solution:

Using WCF extensibility we can achieve this by implementing the - IWsdlExportExtension

public class MyFlatWsdl : IWsdlExportExtension,IEndpointBehavior  

{      

public void ExportContract(WsdlExporter exporter, WsdlContractConversionContext context)

       {

           //throw new NotImplementedException();

       }

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 inwsdl.Types.Schemas)

                       AddImportedSchemas(schema,schemaSet, importsList);

                 if(importsList.Count == 0)

                       return; 

                wsdl.Types.Schemas.Clear();

                foreach(XmlSchema schema in importsList)

                {

                    RemoveXsdImports(schema);                   

                    wsdl.Types.Schemas.Add(schema);

                }

           }

       }

 

private voidAddImportedSchemas(XmlSchema schema, XmlSchemaSet schemaSet, List<XmlSchema> importsList)

       {

           foreach (XmlSchemaImportimport in schema.Includes)

           {

                ICollectionrealSchemas = schemaSet.Schemas(import.Namespace);

                foreach(XmlSchema ixsd in realSchemas)

                {

                    if(!importsList.Contains(ixsd))

                    {

                        importsList.Add(ixsd);

                       AddImportedSchemas(ixsd, schemaSet, importsList);

                    }

                }

           }

       }

       

private voidRemoveXsdImports(XmlSchema schema)

       {

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

           {

                if(schema.Includes[i] is XmlSchemaImport)

                   schema.Includes.RemoveAt(i--);

           }

       }

        

void IEndpointBehavior.AddBindingParameters(ServiceEndpoint endpoint,System.ServiceModel.Channels.BindingParameterCollection
bindingParameters)

       {

        //throw new NotImplementedException();

       }

       

void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint endpoint,System.ServiceModel.Dispatcher.ClientRuntime
clientRuntime)

       {

           //throw new NotImplementedException();

       }

        

void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint endpoint,System.ServiceModel.Dispatcher.EndpointDispatcher
endpointDispatcher)

       {

           //throw new NotImplementedException();

       }

 

void IEndpointBehavior.Validate(ServiceEndpoint endpoint)       

      {           

         //throw new NotImplementedException();       

      }

}

 

 public class MyBehaviorExtension : BehaviorExtensionElement

{

       public overrideType BehaviorType      

     { 

           get { return typeof(MyFlatWsdl);     }

      }

 

protected override object CreateBehavior()

       {

           MyFlatWsdl myBehavior = new MyFlatWsdl();

           return myBehavior;

       }

}

 

Config file changes:

     <extensions>
      <behaviorExtensions>
        <add name="MyEndPointBehavior" type="WcfService2.MyBehaviorExtension, WcfService2"/>
      </behaviorExtensions>
    </extensions>
  
       <service name="WcfService2.Service1">
        <endpoint address="" binding="basicHttpBinding" bindingConfiguration="" behaviorConfiguration="MyBeh"
          name="end" contract="WcfService2.IService1" />
      </service>
  
       <endpointBehaviors>
        <behavior name="MyBeh">
          <MyEndPointBehavior />
        </behavior>
      </endpointBehaviors>
  
 With above code/config changes we can easily expose WCF service with Flat WSDL in 4.0.
 FYI, in WCF 4.5 it is available as "SingleWSDL" option.
  
 Hope this help !