Search code examples
soapwcf-client

Possible to consume badly formed Fault Messages?


I've a WCF client communicating with an unknown server implementation which I have no control over. This client works fine it just doesn't like, what appears to be, incorrectly formed SOAP Fault messages. The messages I receive look like:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">   
    <soap:Header>...</soap:Header>  
    <soap:Body>  
        <soap:Fault>  
            <soap:faultcode>soap:Client</soap:faultcode>  
            <soap:faultstring>...</soap:faultstring>  
            <soap:detail>...</soap:detail>  
        </soap:Fault>  
    </soap:Body>  
</soap:Envelope>  

I believe according to the soap schema the child elements shouldn't be qualified and ned to look like:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">   
    <soap:Header>...</soap:Header>  
    <soap:Body>  
        <soap:Fault>  
            <faultcode>soap:Client</faultcode>  
            <faultstring>...</faultstring>  
            <detail>...</detail>  
        </soap:Fault>  
    </soap:Body> 
</soap:Envelope>

Is there something that I can configure or override so that I can consume messages which arrive in the latter format so that I can consume the fault messages instead of xml exceptions?


Solution

  • I'm cannot recall how I found stumbled across Message Inspectors, but that it how I solved my problem.

    This and this article provided the base for creating the inspector, and what follows is the meat of the inspector:

    public void AfterReceiveReply(ref Message reply, object correlationState)
    {
        if (!reply.IsFault)
            return;
    
        var document = new XmlDocument();
    
        document.Load(reply.GetReaderAtBodyContents());
    
        var navigator = document.CreateNavigator();
        var manager = new XmlNamespaceManager(navigator.NameTable);
    
        manager.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
    
        var it = navigator.Select("//soap:Fault", manager);
    
        if (it.MoveNext() && it.Current.HasChildren && it.Current.MoveToChild(XPathNodeType.Element))
        {
            do
            {
                var c = it.Current;
    
                if (string.IsNullOrEmpty(c.Prefix))
                    continue;
    
                c.ReplaceSelf("<" + c.LocalName + ">" + c.InnerXml + "</" + c.LocalName + ">");
    
                /// we may want to record the detail included inside the detail element, 
                /// it is not reported in the FaultException that is raised.
    
            } while (it.Current.MoveToNext());
        }
    
        var reader = XmlDictionaryReader.CreateDictionaryReader(new XmlNodeReader(document));
    
        reader.MoveToStartElement();
    
        var fixedReply = Message.CreateMessage(reply.Version, null, reader);
    
        fixedReply.Headers.CopyHeadersFrom(reply.Headers);
        fixedReply.Properties.CopyProperties(reply.Properties);
    
        reply = fixedReply;
    }