Messaging-Only: hiding SOAP fault from caller.

In a messaging-only setup, how can I transform a SOAP fault to a contractual response that the caller expects ?

Remaining Questions:

* Is rewriting AckType message context property enough?

* Why is the exception thrown at the send port being rethrown back to the receive port?

* Why is the NACK treated as a message internal to BizTalk and not available to the ports for transformation?

----------------------------

The following is my messaging setup:

* Two-way receive port with a receive location published in IIS.  Receive location is WCF-BasicHttp.  Isolated-host, schema only.

* Two-way send port, WCF-SQL.

The caller expects a response of a strong XML type (let's call it a boolean of TRUE or FALSE) depending if the SQL operation is successful, hiding any faults from the caller.  I have been struggling with mapping the SOAP fault to the caller's message response contract.

I have created a schema based off the SOAP fault:

<?xml version="1.0" encoding="utf-16"?>
<xs:schema xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:b="http://schemas.microsoft.com/BizTalk/2003" elementFormDefault="qualified" targetNamespace="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:import schemaLocation="BTS.xml" namespace="http://www.w3.org/XML/1998/namespace" />
  <xs:annotation>
    <xs:appinfo>
      <b:references>
        <b:reference targetNamespace="http://www.w3.org/XML/1998/namespace" />
      </b:references>
    </xs:appinfo>
  </xs:annotation>
  <xs:element name="Envelope">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="s:Body" />
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="Body">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="s:Fault" />
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="Fault">
    <xs:complexType>
      <xs:sequence>
        <xs:element form="unqualified" name="faultcode" type="xs:NMTOKEN" />
        <xs:element form="unqualified" name="faultstring">
          <xs:complexType mixed="true">
            <xs:attribute ref="xml:lang" use="required" />
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>


Then created a map from the SOAP fault to the caller's message response.  I have tried playing around with maps on both the send and receive ports, as well as different properties within the WCF transports, but the caller always gets back the SOAP fault and not my mapped result:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
   <s:Body>
      <s:Fault>
         <faultcode xmlns:a="http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher">a:InternalServiceFault</faultcode>
         <faultstring xml:lang="en-US">The server was unable to process the request due to an internal error.  For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the &lt;serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework SDK documentation and inspect the server trace logs.</faultstring>
      </s:Fault>
   </s:Body>
</s:Envelope>

ref: http://bveldhoen.wordpress.com/2010/09/05/messaging-only-request-response-correlation/, http://rohitt-sharma.blogspot.com/2011/03/biztalk-tutorial-part-7-using-two-way.htmlhttp://blogical.se/blogs/johan/archive/2008/03/31/delivery-notifications-outside-orchestrations-a-pure-messaging-approach.aspx



July 1st, 2013 4:31pm

So I've been looking at this for the past 5 hours...

I stumbled on this http://blogical.se/blogs/johan/archive/2010/04/08/biztalk-and-soap-fault-version-issues.aspx.

Then this http://connectedpawns.wordpress.com/2010/04/09/synchronous-web-services-calls-and-faults/#comments

Then this http://technet.microsoft.com/en-us/library/cc950532(v=bts.10).aspx

Some of this http://blogs.msdn.com/b/kevinsmi/archive/2004/07/03/172574.aspx

Then finally I saw this:

"In the case where a solicit-response send port is connected to a request-response receive port, the receive port gets either a response message (if the transmission succeeds) or a NACK (if the transmission fails), regardless of whether the failed message has been routed."

Theory aside I started looking at the suspended service instances.  I turned on the XmlTransmit send pipeline on the receive port and was able to see the pipeline execute successfully with a NACK.  Still, the map didn't get applied and the caller receives a SOAP fault.

It's almost like the NACK never surfaces as a response message.  Next I will research this a little more and see what a custom pipeline can provide.


Free Windows Admin Tool Kit Click here and download it now
July 1st, 2013 8:28pm

Hi Chris,

Based on my understanding I believe you can achieve the scenario you have described by doing something similar to this:


If you have any questions please feel free to ask.

July 1st, 2013 8:42pm

Yes, from what I've been reading this should be a piece of cake with an orchestration.  But what about no orchestration?
Free Windows Admin Tool Kit Click here and download it now
July 1st, 2013 10:20pm

I'm done with this for today...

Original stack makes me wonder if and why the SOAP fault that is thrown on the send port bubbles back to the receive port.

at Microsoft.BizTalk.Adapter.Wcf.Runtime.BizTalkAsyncResult.End()&#xD;
   at Microsoft.BizTalk.Adapter.Wcf.Runtime.BizTalkServiceInstance.EndOperation(IAsyncResult result)&#xD;
   at Microsoft.BizTalk.Adapter.Wcf.Runtime.BizTalkServiceInstance.Microsoft.BizTalk.Adapter.Wcf.Runtime.ITwoWayAsync.EndTwoWayMethod(IAsyncResult result)&#xD;
   at AsyncInvokeEndEndTwoWayMethod(Object , Object[] , IAsyncResult )&#xD;
   at System.ServiceModel.Dispatcher.AsyncMethodInvoker.InvokeEnd(Object instance, Object[]&amp; outputs, IAsyncResult result)&#xD;
   at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeEnd(MessageRpc&amp; rpc)&#xD;
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage7(MessageRpc&amp; rpc)&#xD;
   at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)

Pipe code:

public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg)
        {
            string nsBtsSysProps = "http://schemas.microsoft.com/BizTalk/2003/system-properties";
            string ackTypePropName = "AckType";
            string ACK = "ACK";
            string NACK = "NACK";
            
            if (NACK == (string)pInMsg.Context.Read(ackTypePropName, nsBtsSysProps))
            {
                string xmlResponse = @"<ns0:Response xmlns:ns0='http://x.com/iconnect/response'>
                                      <MessageId>{0}</MessageId> 
                                      <Detail>Service Fault on BizTalk MessageId: {1}</Detail> 
                                      </ns0:Response>";
                
                pInMsg.Context.Write(ackTypePropName, nsBtsSysProps, ACK);

                IBaseMessagePart bodyPart = pInMsg.BodyPart;
                Stream bodyStream = bodyPart.GetOriginalDataStream();

                XmlDocument xDoc = new XmlDocument();
                xDoc.LoadXml(string.Format(xmlResponse, Guid.Empty.ToString(), pInMsg.MessageID.ToString()));
                byte[] xBytes = System.Text.Encoding.ASCII.GetBytes(xDoc.InnerXml);
                MemoryStream xStream = new MemoryStream();
                xStream.Write(xBytes, 0, xBytes.Length);
                xStream.Position = 0;
                bodyPart.Data = xStream;
            }

            return pInMsg;
        }

Response with pipe: (the original inbound message still remains as a suspended service instance)

HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
Server: Microsoft-IIS/7.5
Date: Mon, 01 Jul 2013 19:33:59 GMT
Content-Length: 327

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><ns0:Response xmlns:ns0="http://x.com/iconnect/response"><MessageId>00000000-0000-0000-0000-000000000000</MessageId><Detail>Service Fault on BizTalk MessageId: 926e0654-da53-4ef3-9bae-64b9dcc1993a</Detail></ns0:Response></s:Body></s:Envelope>

Response without pipe:

HTTP/1.1 500 Internal Server Error
Content-Length: 730
Content-Type: text/xml; charset=utf-8
Server: Microsoft-IIS/7.5
Date: Mon, 01 Jul 2013 19:36:05 GMT

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><s:Fault><faultcode xmlns:a="http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher">a:InternalServiceFault</faultcode><faultstring xml:lang="en-US">The server was unable to process the request due to an internal error.  For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the &lt;serviceDebug&gt; configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework SDK documentation and inspect the server trace logs.</faultstring></s:Fault></s:Body></s:Envelope>


July 1st, 2013 10:39pm

This solution seems to be working ok so far... I think I might have to put a call into MS about this... unless someone is already looking at it?
Free Windows Admin Tool Kit Click here and download it now
July 2nd, 2013 4:13pm

It doesn't make sense to hide it. Why?

Let's look at the whole message way between client and web-service.

ClientCode - WCFProxy - Channel - WCFDispatcher - ServiceCode

If something wrong happened between Client and Service code, say timeout in a channel, the SoapFault will be created and returned to the client. The service cannot change this behavior! Say the client could not connect to the channel, again client gets the SoapFault. If service code throw unhandled exception it will be serialized and returned to the client. If it is a C# client, this exception will/can be deserialized back to the original exception in the client code by Proxy.

So you could catch some service exceptions on the service and return a standard response, but you cannot catch all possible exceptions. So, as a result, client still has to manage SoapFault and PLUS the "processed" exception. So developer creates additional code on service AND on the client code for nothing.

July 2nd, 2013 7:25pm

Leonid, you know what, you're right.  After reading basicprofile 1.1, SOAP faults are the standard.  So I guess instead of me reinventing a fault strategy, how could I include custom information in a SOAP fault?  
Free Windows Admin Tool Kit Click here and download it now
July 3rd, 2013 8:16am

detail section of the Soap Fault is intended to carry on the application specific information. We could put here almost anything, for example several messages and message context. Of course the service and client should use the same conventions and contracts to share these messages. Just be prepared to find here not your custom information but information from channel etc.
July 3rd, 2013 11:20am

Right, so if I wanted to include InterchangeID in my SOAP fault for correlation reasons, it looks like I will still have to have a pipeline that recognized the NACK and inserts detail into the SOAP fault.  Since it seems like the SOAP fault is passed around by biztalk internally and not as a message.
Free Windows Admin Tool Kit Click here and download it now
July 3rd, 2013 1:24pm

It is processed in a MessageBox as ordinary message.

A special tooling is used to consume fault messages in orchestration (any fault messages not only SoapFault). You add a New Fault enpoint to the operation of a port shape (which is inbound), create an exception handler block, etc. Similar tooling is used to produce the fault messages. You add a New Fault enpoint to the operation of a port shape (which is outbound now), create a Fault message (place your correlation parameter or InterchangeID here) and send it to this New Fault, etc. No additional pipelines here.

If your solution is just port to port messaging, you don't have to bother with it.


July 3rd, 2013 2:06pm

Yes I am only port-port.  

Are you saying that the SOAP fault generated at the two-way send port will be passed back to the two-way receive port on its way back?  If so, should I create a map on top of the SOAP fault schema and demote as necessary?  Would I be going after /Envelope/Body/Fault/faultstring/text() as the source schema at the receive port going outbound/back to caller?

example of fault at caller:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
   <s:Body>
      <s:Fault>
         <faultcode xmlns:a="http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher">a:InternalServiceFault</faultcode>
         <faultstring xml:lang="en-US"><![CDATA[<?xml version="1.0" encoding="utf-8"?><SOAP:Envelope xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/" SOAP:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP:Body><SOAP:Fault><faultcode>Microsoft BizTalk Server Negative Acknowledgment </faultcode><faultstring>An error occurred while processing the message, refer to the details section for more information </faultstring><faultactor>mssql://us09sqldev.maclean-fogg.com//JDE_CNV?</faultactor><detail><ns0:NACK Type="NACK" xmlns:ns0="http://schema.microsoft.com/BizTalk/2003/NACKMessage.xsd"><NAckID>{B28FC451-9D6E-4593-A550-5FD907A41B57}</NAckID><ErrorCode>0xc0c0167a</ErrorCode><ErrorCategory>0</ErrorCategory><ErrorDescription>System.Data.SqlClient.SqlException (0x80131904): Violation of PRIMARY KEY constraint 'F47171_PK'. Cannot insert duplicate key in object 'CNVDTA.F47171'. The duplicate key value is (   12, 2.13399e+006, PS).
The statement has been terminated.

Server stack trace: 
   at System.Runtime.AsyncResult.End[TAsyncResult](IAsyncResult result)
   at System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.End(SendAsyncResult result)
   at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
   at System.ServiceModel.Channels.ServiceChannel.EndRequest(IAsyncResult result)

Exception rethrown at [0]: 
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData&amp; msgData, Int32 type)
   at System.ServiceModel.Channels.IRequestChannel.EndRequest(IAsyncResult result)
   at Microsoft.BizTalk.Adapter.Wcf.Runtime.WcfClient`2.RequestCallback(IAsyncResult result)
ClientConnectionId:e1905ce6-1a4d-405b-834a-3a5102f6e90d</ErrorDescription></ns0:NACK></detail></SOAP:Fault></SOAP:Body></SOAP:Envelope>]]></faultstring>
         <detail>
            <ExceptionDetail xmlns="http://schemas.datacontract.org/2004/07/System.ServiceModel" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
               <HelpLink i:nil="true"/>
               <InnerException i:nil="true"/>
               <Message><![CDATA[<?xml version="1.0" encoding="utf-8"?><SOAP:Envelope xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/" SOAP:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP:Body><SOAP:Fault><faultcode>Microsoft BizTalk Server Negative Acknowledgment </faultcode><faultstring>An error occurred while processing the message, refer to the details section for more information </faultstring><faultactor>mssql://us09sqldev.maclean-fogg.com//JDE_CNV?</faultactor><detail><ns0:NACK Type="NACK" xmlns:ns0="http://schema.microsoft.com/BizTalk/2003/NACKMessage.xsd"><NAckID>{B28FC451-9D6E-4593-A550-5FD907A41B57}</NAckID><ErrorCode>0xc0c0167a</ErrorCode><ErrorCategory>0</ErrorCategory><ErrorDescription>System.Data.SqlClient.SqlException (0x80131904): Violation of PRIMARY KEY constraint 'F47171_PK'. Cannot insert duplicate key in object 'CNVDTA.F47171'. The duplicate key value is (   12, 2.13399e+006, PS).
The statement has been terminated.

Server stack trace: 
   at System.Runtime.AsyncResult.End[TAsyncResult](IAsyncResult result)
   at System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.End(SendAsyncResult result)
   at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
   at System.ServiceModel.Channels.ServiceChannel.EndRequest(IAsyncResult result)

Exception rethrown at [0]: 
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData&amp; msgData, Int32 type)
   at System.ServiceModel.Channels.IRequestChannel.EndRequest(IAsyncResult result)
   at Microsoft.BizTalk.Adapter.Wcf.Runtime.WcfClient`2.RequestCallback(IAsyncResult result)
ClientConnectionId:e1905ce6-1a4d-405b-834a-3a5102f6e90d</ErrorDescription></ns0:NACK></detail></SOAP:Fault></SOAP:Body></SOAP:Envelope>]]></Message>
               <StackTrace>at Microsoft.BizTalk.Adapter.Wcf.Runtime.BizTalkAsyncResult.End()
   at Microsoft.BizTalk.Adapter.Wcf.Runtime.BizTalkServiceInstance.EndOperation(IAsyncResult result)
   at Microsoft.BizTalk.Adapter.Wcf.Runtime.BizTalkServiceInstance.Microsoft.BizTalk.Adapter.Wcf.Runtime.ITwoWayAsync.EndTwoWayMethod(IAsyncResult result)
   at AsyncInvokeEndEndTwoWayMethod(Object , Object[] , IAsyncResult )
   at System.ServiceModel.Dispatcher.AsyncMethodInvoker.InvokeEnd(Object instance, Object[]&amp; outputs, IAsyncResult result)
   at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeEnd(MessageRpc&amp; rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage7(MessageRpc&amp; rpc)
   at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)</StackTrace>
               <Type>Microsoft.BizTalk.Adapter.Wcf.Runtime.BizTalkNackException</Type>
            </ExceptionDetail>
         </detail>
      </s:Fault>
   </s:Body>
</s:Envelope>

Free Windows Admin Tool Kit Click here and download it now
July 3rd, 2013 2:13pm

I would create an additional send port with a Filter which subscribe to this Fault. etc.

But, frankly speaking, I would not use BizTalk here at all. As I can see you are trying to create a wrapping web-service.

It is easier to create it in C#. In this case all error handling would be under your full control. It is really easy, VS gives a lot of tooling for this task.

As a result you also get a web-service (as it is with BizTalk) but it would be so simple. The mapping part... yeah, if your format transformation is complex it could be easy in BizTalk (but if it is really complex).

July 3rd, 2013 2:43pm

Speaking of C#, couldn't I create a service behavior such as IErrorHandler or others from here: http://blogs.msdn.com/b/carlosfigueira/archive/2011/03/14/wcf-extensibility.aspx and include it in my BTS WCF-xxx adapters?

However, from any of these extension points will I have access to the message or BTS service context, or would I need to write  the BTS.InterchangeId property out of process to once again include it in the returning SOAP fault?

I'm going to try to create a map based on the SOAP fault schema to see once again if I can catch it as a message at the port.

Free Windows Admin Tool Kit Click here and download it now
July 3rd, 2013 4:26pm

Speaking of C#, couldn't I create a service behavior such as IErrorHandler or others from here: http://blogs.msdn.com/b/carlosfigueira/archive/2011/03/14/wcf-extensibility.aspx and include it in my BTS WCF-xxx adapters?

+++ Yes, you can do this.

However, from any of these extension points will I have access to the message or BTS service context, or would I need to write  the BTS.InterchangeId property out of process to once again include it in the returning SOAP fault?

+++ check the "Propagate fault message" option on the Messages tab of the WCF Transport Properties window. In such case the send port will automatically take care of the correlation of the fault message back to the receive port.

Propagate fault message

Select this check box to route the message that fails outbound processing to a subscribing application (such as another receive port or orchestration schedule). Clear the check box to suspend failed messages and generate a negative acknowledgment (NACK). This property is valid only for solicit-response ports. 

I'm going to try to create a map based on the SOAP fault schema to see once again if I can catch it as a message at the port.


July 3rd, 2013 5:21pm

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics