I'm calling a SOAP web service from .NET 4.5 using C#, and I can't understand how to catch the detail of an untyped FaultException
.
If the web service experiences an error, I get a message like this one:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<S:Fault xmlns:ns4="http://www.w3.org/2003/05/soap-envelope">
<faultcode>GSX.SYS.003</faultcode>
<faultstring>Multiple error messages exist. Please check the detail section.</faultstring>
<detail>
<operationId>HqrCoRMbtKczWFH2WMuFJGe</operationId>
<errors>
<error>
<code>ENT.UPL.005</code>
<message>User ID is required for authentication.</message>
</error>
<error>
<code>ENT.UPL.005</code>
<message>Password is required for authentication.</message>
</error>
<error>
<code>ENT.UPL.005</code>
<message>Sold-To is required for authentication.</message>
</error>
</errors>
</detail>
</S:Fault>
</S:Body>
</S:Envelope>
To catch the error, I wrap the call in a try/catch block and catch the FaultException
:
try
{
// Do something.
}
catch (FaultException faultException)
{
var messageFault = faultException.CreateMessageFault();
// ???
throw;
}
The problem lies in the // ???
line: how can I access the detail of the FaultException
, given that I can't use messageFault.GetDetail<T>()
because the detail is untyped?
For the code and the reason I don't have a problem, I get them with messageFault.Code.Name
and messageFault.Reason.Translations.Single().Text
.
What I've been able to concoct is this:
var stringWriter = new StringWriter();
var xmlTextWriter = new XmlTextWriter(stringWriter);
var messageFault = faultException.CreateMessageFault();
messageFault.WriteTo(xmlTextWriter, EnvelopeVersion.Soap12);
var stringValue = Convert.ToString(stringWriter);
var nameTable = new NameTable();
var xmlNamespaceManager = new XmlNamespaceManager(nameTable);
xmlNamespaceManager.AddNamespace("soap", "http://www.w3.org/2003/05/soap-envelope");
var xmlDocument = XDocument.Parse(stringValue);
var operationId =
xmlDocument
.XPathSelectElement("/soap:Fault/soap:Detail/operationId", xmlNamespaceManager)
.Value;
var errors =
xmlDocument
.XPathSelectElements("/soap:Fault/soap:Detail/errors/error", xmlNamespaceManager)
.Select(element => new
{
Code = element.XPathSelectElement("code").Value,
Message = element.XPathSelectElement("message").Value
})
.ToArray();
But null reference and encoding issues aside, it's a monster.
There must be a saner way to get the details.
You can read the detail as an XML type, eg. XElement or XmlElement and process it using XPath, Linq to XML or just treat it as string.
In a similar situation I use the following code to get at the contents of an admittedly simple details element:
var detail = fault.GetDetail<XElement>();
return detail.Value;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With