none
Serialize ServiceModel.Channels.Message to FaultException Type RRS feed

  • Question

  • Hello!  I can receive Fault Exceptions similar to the following:

    <SOAP-ENV:Body>
          <SOAP-ENV:Fault>
             <faultcode>SOAP-ENV:Server</faultcode>
             <faultstring>Fault returned by invoked service</faultstring>
             <faultactor>blah</faultactor>
             <detail>
                <esbflt:MyFaultType xmlns:esbflt="http://mynamespace.com/something">
                   <ErrorCodeESB>10005</ErrorCodeESB>
                   <ErrorDescriptionESB>Unknown error. See AppDetail for more information on this error.</ErrorDescriptionESB>
                   <AppDetails>
                      <AppDetail>
                         <ErrorCodeApp/>
                         <ErrorDescriptionApp>Here is actually what went bad.</ErrorDescriptionApp>
                      </AppDetail>
                   </AppDetails>
                </esbflt:MyFaultType>
             </detail>
          </SOAP-ENV:Fault>
       </SOAP-ENV:Body>

    I want to catch these Faults in the AfterReceiveReply of the IClientMessageInspector and send back the ErrorDescriptionApp element "Here is actually what went bad.".

    I can do it by enumerating through the elements of the reply looking for the XElement names.  Something like:

    XElement rootTag = XElement.Parse(reply.ToString());
    IEnumerable<XElement> faultTags = rootTag.DescendantsAndSelf();
    ....other code here

    But that seems inefficient to me.

    I would prefer to do it like I would from within the WCF method:

    catch (Exception err)
    {
          Type exceptionType = err.GetType();
           if (exceptionType.IsGenericType && exceptionType.GetGenericTypeDefinition() == typeof(FaultException<>))
          {
              PropertyInfo prop = exceptionType.GetProperty("Detail");
              object propValue = prop.GetValue(err, null);
              if (propValue is MyFaultType)
              {
                  MyFaultType ft = (MyFaultType)propValue;

    Any assistance on how I can accomplish that in the IClientMessageInspector would be appreciated!

    Thank you very much!

     
    Thursday, October 5, 2017 9:45 PM

Answers

  • Hi Hiline,

    I am afraid we could not use the error handling directly at IClientMessageInspector. We still need to loop through the elements and get detail element.

    But, I think we could try to convert detail part to Fault object instead of loop through the elements in detail element.

    Here is a simple code:

    1. Define Fault Object

        [DataContract]
        public class MyFaultMessage
        {
            public MyFaultMessage(string message)
            {
                Message = message;
            }
    
            [DataMember]
            public string Message { get; set; }
        }
    

    2. Return Fault Exception

            public string GetData(int value)
            {
                throw new FaultException<MyFaultMessage>(new MyFaultMessage("An error occurred."));
                return string.Format("You entered: {0}", value);
            }
    

    3. Get Fault Object at IClientMessageInspector

        public class SimpleMessageInspector : IClientMessageInspector
        {
            public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
            {
                // Implement this method to inspect/modify messages after a message  
                // is received but prior to passing it back to the client   
                Console.WriteLine("AfterReceiveReply called");
                if (reply.IsFault)
                {
                    // Create a copy of the original reply to allow default WCF processing
                    var buffer = reply.CreateBufferedCopy(Int32.MaxValue);
    
                    // Create a copy to work with
                    var copy = buffer.CreateMessage();
    
                    // Restore the original message 
                    reply = buffer.CreateMessage();
                    var exception = ReadFaultDetail(copy);                
                }
            }        
            private static object ReadFaultDetail(Message reply)
            {
                const string detailElementName = "detail";
                
                using (XmlDictionaryReader reader = reply.GetReaderAtBodyContents())
                {
                    System.Diagnostics.Debug.WriteLine(reader.ReadContentAsString());
                    // Find <soap:Detail>
                    while (reader.Read())
                    {
                        System.Diagnostics.Debug.WriteLine(reader.ToString());
                        if (reader.NodeType == XmlNodeType.Element && detailElementName.ToUpper().Equals(reader.LocalName.ToUpper()))
                        {
                            break;
                        }
                    }
                    if (reader.EOF)
                    {
                        return null;
                    }
    
                    // Move to the contents of <soap:Detail>
                    if (!reader.Read())
                    {
                        return null;
                    }            
                    
                    try
                    {
                        DataContractSerializer dcs = new DataContractSerializer(typeof(MyFaultMessage));
    
                        MyFaultMessage fault= (MyFaultMessage)dcs.ReadObject(reader);
    
                        return fault;
                    }
    
                    catch (SerializationException ex)
                    {
                        return new CommunicationException("SerializationException", ex);
                    }
                }
            }
            public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel)
            {
                // Implement this method to inspect/modify messages before they   
                // are sent to the service  
                Console.WriteLine("BeforeSendRequest called");
                return null;
            }
        }
    

    Best Regards,

    Edward


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    • Marked as answer by Hiline1961 Friday, October 6, 2017 3:29 PM
    Friday, October 6, 2017 4:50 AM

All replies

  • Hi Hiline,

    I am afraid we could not use the error handling directly at IClientMessageInspector. We still need to loop through the elements and get detail element.

    But, I think we could try to convert detail part to Fault object instead of loop through the elements in detail element.

    Here is a simple code:

    1. Define Fault Object

        [DataContract]
        public class MyFaultMessage
        {
            public MyFaultMessage(string message)
            {
                Message = message;
            }
    
            [DataMember]
            public string Message { get; set; }
        }
    

    2. Return Fault Exception

            public string GetData(int value)
            {
                throw new FaultException<MyFaultMessage>(new MyFaultMessage("An error occurred."));
                return string.Format("You entered: {0}", value);
            }
    

    3. Get Fault Object at IClientMessageInspector

        public class SimpleMessageInspector : IClientMessageInspector
        {
            public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
            {
                // Implement this method to inspect/modify messages after a message  
                // is received but prior to passing it back to the client   
                Console.WriteLine("AfterReceiveReply called");
                if (reply.IsFault)
                {
                    // Create a copy of the original reply to allow default WCF processing
                    var buffer = reply.CreateBufferedCopy(Int32.MaxValue);
    
                    // Create a copy to work with
                    var copy = buffer.CreateMessage();
    
                    // Restore the original message 
                    reply = buffer.CreateMessage();
                    var exception = ReadFaultDetail(copy);                
                }
            }        
            private static object ReadFaultDetail(Message reply)
            {
                const string detailElementName = "detail";
                
                using (XmlDictionaryReader reader = reply.GetReaderAtBodyContents())
                {
                    System.Diagnostics.Debug.WriteLine(reader.ReadContentAsString());
                    // Find <soap:Detail>
                    while (reader.Read())
                    {
                        System.Diagnostics.Debug.WriteLine(reader.ToString());
                        if (reader.NodeType == XmlNodeType.Element && detailElementName.ToUpper().Equals(reader.LocalName.ToUpper()))
                        {
                            break;
                        }
                    }
                    if (reader.EOF)
                    {
                        return null;
                    }
    
                    // Move to the contents of <soap:Detail>
                    if (!reader.Read())
                    {
                        return null;
                    }            
                    
                    try
                    {
                        DataContractSerializer dcs = new DataContractSerializer(typeof(MyFaultMessage));
    
                        MyFaultMessage fault= (MyFaultMessage)dcs.ReadObject(reader);
    
                        return fault;
                    }
    
                    catch (SerializationException ex)
                    {
                        return new CommunicationException("SerializationException", ex);
                    }
                }
            }
            public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel)
            {
                // Implement this method to inspect/modify messages before they   
                // are sent to the service  
                Console.WriteLine("BeforeSendRequest called");
                return null;
            }
        }
    

    Best Regards,

    Edward


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    • Marked as answer by Hiline1961 Friday, October 6, 2017 3:29 PM
    Friday, October 6, 2017 4:50 AM
  • Thank you, Edward!
    Friday, October 6, 2017 3:30 PM