Unqualified root-element issue

There is problem with the signature of WCF when the service uses a root element that is in unqualified form.

Problem description

XML namespaces come in 2 forms: qualified and unqualified. In most cases the elements are qualified and the attributes are by default unqualified. This means the elements have prefixes (can be default) and attributes haven't. This means you know exactly which namespace an element belongs too, but attributes can belong to any namespace (the namespace of the element it belongs to being the most likely).

For reasons unknown to me, eHealth decided that unqualified elements is a good thing. In WCF this generates XML messages that have namespace prefixes defined for unused namespaces (this is at least as strange as unqualified elements itself). These unused namespace prefixes cause a canonicalization issue between .Net/JavaApache and Java BEA signature libraries.

You could try to convince Microsoft not to generate the unused prefix declarations and/or Oracle to align its signature library with the .Net and Java Apache signature library. Actually I tried the latter for a different project when BEA was still independent and failed. Maybe you have better luck then me...

Warning I did not have the opportunity to verify if the problem also exists for the DataContractSerializer, currently it only applies when using the XmlSerializer.

Problem workaround

I did find a workaround that avoids the generation of the unused namespace prefix declarations.

Principal

Because WCF generates not-so-logic xml we no longer let it generate the xml, instaid we generate it ourself. Because of this you can no longer add service references as you are used to, but instead you must write them by hand. By using ClientBase as the base class, all security and other stuff is still handled by WCF. You could write the input and output by hand to, but I prefer to extract the xsd out of the wsdl and generate the classes from that. With a little helper class that is included in the library I managed to generate requests that have a valid signature according to BEA.

This approach limits the amount of manual work, but you will have to write the port interface and client class yourself. How to do this and the parts above is described next.

Step by Step

  1. Generate input/output classes
    1. Extract the schema of the wsdl
      1. Open the wsdl in your favorite browser
      2. Extract the schema definition (in the type-element)
        • If present, copy the schema in a new file
        • If a wsdl-include open the link in your browser and copy the schema in a new file
      3. Resolve all includes
        1. Open the include link and save schema in new file (next to previous file)
        2. Change link with name of new file
    2. Generate the code from the schema by running xsd with
      • The name of the schema file
      • /c to generate classes
      • /n:<<ns to specify the namespace
  2. Write the port interface
    1. Define the interface
      1. Give it a name similar or equal to the port type of the WSDL
      2. Give it a ServiceContract-Attribute with
        • A namespace equal to the target namespace of the WSDL
        • A name equal to the name of the port in the WSDL
    2. Define one method
      1. Give it a generic name, e.g. "process"
      2. Give it a OperationContract-Attribute with
        • An Action equal to "*"
        • A ReplayAction equal to "*"
  3. Write the client class
    1. Define the class
      1. Give it a name similar to the port with a prefix and/or suffix that inherits from ClientBase<>
      2. Create all constructors, all calling the base constructor with the same params
        • Default constructor, no params
        • Config name as param
        • Config name and address (string) as param
        • Config name and address as param
        • Binding and address as param
      3. Create a method for each method in the port type of the WSDL
        1. Use the class that correspond to the input/output of the WSDL operation for the param and return type
        2. Use the Message.CreateMessage to convert the input into a message
          • Most eHealth services use SOAP1.1, so specify this
          • Check the port binding of the WSDL to find the soap action
          • You must us the XmlSerializerObjectSerializer-Class as serializer that is part of this library (This is the crucial part)
        3. Send the message via the Channel-Property, it implements the port
        4. Check if the response if a fault, with the IsFault-Property
          1. If a fault, create a fault with the MessageFault.CreateFault method and FaultException-Constructor
        5. Use the Message.GetBody<> method convert the response into objects.

When there is a sample for you service, just copy the code in the "Service" directory. Don't forget to follow the LGPL rules when you do!

Last edited Dec 2, 2010 at 7:18 PM by egelke, version 1

Comments

No comments yet.