A developer in the same team made me aware of an issue they were seeing where if the BizTalk Send Port returned an exception the Client that called the ReceivePort would get a response that was interpreted as null instead of as an exception.
Now this is a synchronous service call without any orchestration, where BizTalk is just a broker of the web service calls. What we want out of it is this:
- For exceptions throw by the backend service to get relayed back to the original caller.
- For no suspended messages to be visible in BizTalk when an exception occurs with the backend service – so that operations won’t be bothered with removing them.
- For operations to get notified of all other exceptions, ie suspended messages.
Now this is all quite easy, all you need to do is make sure the propagate fault flag is checked on the send port adapter settings:
(somewhat shortened dialog)
What can happen though is that when the SOAP version does not match between what the Send Port receives and relays and what the client that called the Receive Port/Location is expecting is that the Fault ends up not being correctly formatted and thus the client is unable to interpret it.
This happens when you use the BasicHttp adapter or BasicHttpBinding with the Custom Adapter on the Receive Port and NetTcp (or WSHttp or… etc) on the Send Port (or vice versa). It happens because the SOAP version that BasicHttp uses is different than that used by the other WCF Adapters.
A sample SOAP 1.1 Fault look like this:
<s:Fault>
<faultcode>s:Client</faultcode>
<faultstring xml:lang=”sv-SE”>You entered 666</faultstring>
</s:Fault>
(where the namespace s is defined on the envelope and points to xmlns:s=”http://schemas.xmlsoap.org/soap/envelope/”)
While a sample SOAP 1.2 Fault look like this:
<s:Fault xmlns:s=”http://www.w3.org/2003/05/soap-envelope”>
<s:Code>
<s:Value>s:Sender</s:Value>
</s:Code>
<s:Reason>
<s:Text xml:lang=”sv-SE”>You entered 666</s:Text>
</s:Reason>
</s:Fault>
In our scenario, with the type of metadata that BizTalk exposes for the service, what happens at the client is that what is actually a Fault instead gets interpreted as a valid response, but since the element that represents the response, let’s call it GetDataResponse just for sake of illustration, is missing – the client will interpret it as a null response. Highly unwanted.
The solution is to make sure that the Fault response is valid for the version of SOAP used by the receive side adapter, that is; do a transformation. A transformation can be done in many places: a map, a pipeline component, a WCF Message Inspector. In my case I prefer to place the component resolving the problem as close as possible to the thing causing the problem – which here means I opt for the Message Inspector, but a map might be the easiest for many. I might add the implementation of the message inspector in a later post, but for now, below is some custom xslt you can use in a map (or elsewhere) (if you do not use custom xslt you might get complaints from the client that the namespace prefixes are incorrect for reserved namespaces) – it maps from SOAP 1.1 to SOAP 1.2 (from BasicHttp to others, like NetTcp). You might perhaps also have to map in the other direction.
<xsl:stylesheet xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”
xmlns:msxsl=”urn:schemas-microsoft-com:xslt”
xmlns:var=”http://schemas.microsoft.com/BizTalk/2003/var”
exclude-result-prefixes=”msxsl var s0″ version=”1.0″
xmlns:s0=”http://schemas.xmlsoap.org/soap/envelope/”
xmlns:xml=”http://www.w3.org/XML/1998/namespace”
xmlns:s=”http://www.w3.org/2003/05/soap-envelope”>
<xsl:output omit-xml-declaration=”yes” method=”xml” version=”1.0″ />
<xsl:template match=”/”>
<xsl:apply-templates select=”/s0:Fault” />
</xsl:template>
<xsl:template match=”/s0:Fault”>
<s:Fault>
<s:Code>
<s:Value>
<xsl:value-of select=”faultcode/text()” />
</s:Value>
</s:Code>
<s:Reason>
<s:Text xml:lang=”sv-se”>
<xsl:value-of select=”faultstring/text()” />
</s:Text>
</s:Reason>
<xsl:if test=”faultactor”>
<s:Role>
<xsl:value-of select=”faultactor/text()” />
</s:Role>
</xsl:if>
<xsl:for-each select=”detail”>
<s:Detail>
<xsl:value-of select=”./text()” />
</s:Detail>
</xsl:for-each>
</s:Fault>
</xsl:template>
</xsl:stylesheet>
I followed up on this with a post on why this doesn’t apply to the WCF-SQL adapter at /2010/04/15/send-failures-nacks-and-soap-faults/.
I will do an additional follow up once I implement the MessageInspector solution.
LikeLike