Wcf supports two WS-Addressing versions – the August 2004 draft and the actual standard v 1.0. There is another wsa version which is used by some soap stacks (who said wse 2?) – the march 2004 draft. To turn this fact to a more practical problem, you might need to write a wcf client to a (non Wcf) service which expects a request like this:
One common patterns with web services is the router service. It can hide routing logic from the client or help with load balancing. Wcf 4 ships with libraries and samples to help build such a router very quickly.
Stephen Liedig had notified me recently on a problem which happens when a gSoap client calls a Wcf router. gSoap is a very popular CPP web services stack.
This was the deployment:
The gSoap client was sending this message to the router:
<soap:Envelope xmlns="http://tempuri.org/" xmlns:p="http://myNs/" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://tempuri.org/:user. The InnerException message was 'Element 'http://tempuri.org/:user' contains data of the ':User' data contract. The deserializer has no knowledge of any type that maps to this contract. Add the type corresponding to 'User' to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding it to the list of known types passed to DataContractSerializer.'. Please see InnerException for more details.
We need to ask two questions: Why this error happens? Why not with Http?
First we must take a look in the message that the router sent to the service. Here is how it appears in the Wcf log:
Something is missing – the “p” prefix declaration (xmlns:p=http://myNs/) which was present in the message form the client to the router does not appear. This makes this message invalid as it references the undeclared prefix "p" in the "p:User" derived type attribute. But why did the router sent an invalid message when the client sent it a good one? And why not with Http?
Let’s take a look at how Wcf routes messages. We can use the reflector to inspect System.ServiceModel.Routing.SoapProcessingBehavior+SoapProcessingInspector.MarshalMessage():
Wcf uses this logic:
Since the “p” prefix is not defined under the body but under the root “envelope” element, this prefix is not sent which makes the message invalid. This explains the netTcp bug.
Why Wcf behaves in this way?
Dismissing this behavior as a bug will miss an important discussion. Consider the naïve fix: Parse each attribute under the body and search for the something:something pattern. Since the router has no information about the message semantics it has no way to know if the pattern relates to a prefix or is just a string which looks like this. Moreover doing this would be a huge performance hit, especially with big messages.
What should be the correct behavior?
In order to take both the functional and performance requirements into account, I would recommend the following approach:
Yes, I can think of a few edge cases where this scheme fails. But a more comprehensive solution would cost in performance.
As it stands now, the default gSoap client fails to call the default Wcf router when protocol bridging is used, which is a real interoperability problem.
And Just to clarify, this web service does not define any derived (=inherited = known) types. For some reason the gSoap default message generation logic uses the xsi:type even on base types. This is not forbidden since it uses the correct type name, although there is no real reason to do it. Anyway this makes this case quite common and not limited to services with derived types.
And, yes, this is one of the reasons not everyone likes Xml.