Introduction

This project solves handling SoapException Web Service (ASMX) issues in WCF based client applications. It is very common in companies that work with external partners today to use Web Services as the means of sharing information. This is true in healthcare, banking, and many other company specific applications. A fundamental problem for clients working with external Web Service partners is that the client company normally has no control over what is returned by the Web Service and, in particular, the exceptions. It is truly a WYSIWYG situation.

Background

It matters little how or why a service is implemented the way it is to the "under the gun - get it done" developer. I have seen so many forum posts that simply tell you to do this or that on the Web Service and "problem-solved!" Obviously, these people have luxuries that the rest of us don't enjoy. Normal developers can't just run out and change the Web Service, it usually doesn't even belong to us. So you can see the dilemma we face. How do we get SoapException information into a WCF based client from an old SOAP or ASMX based Web Service?

For those who don't know, WCF shields us from the problems of handling SOAP - good/bad... depends on which day you ask me. On the whole, in my humble opinion, WCF handling of exceptions is far superior than other choices, but that is another future article. The WCF SOAP shield (wraps) the SoapException into a FaultException. For those of us who must have the "detail" section of the SoapException are simply out of luck, or so it seems. Microsoft, for reason only they know, decided to not surface the "detail" section. They did however conveniently provide a HasDetail property.

So we know from the HasDetail that the Web Service is providing us with additional SoapException information, but we can't get to it. Now, talking about a real "chaps my butt" issue. This is one of those. I worked on the problem, and found several ways to approach the problem without resorting to dumping WCF. The good things that WCF provides outweighs the exception issue. I found a thread on the MSDN Forum - Problem Catching SoapExceptions. Seems I am not the only developer with this issue. While working on the forum, John Saunders (MVP) came up with a compact workaround; while not utopia, it is certainly effective. Below, I am going to discuss several ways to tackle the issue of SoapException to FaultException management.

Using the Code

I have provided a complete test ASMX Web Service and a WCF SoapClientTest project. Instructions on setting up the solution (.NET 3.5 SP1 Visual Studio 2008) are in the zip file.

The real world problem we are trying to solve is to handle SOAP exceptions thrown by the Web Service. I must inject for the "purest", you should as a competent developer study the Web Service API and code against the API to avoid exceptions in the first place. For example, suppose a service has a requirement that a client ID must exist before you attempt to write to a record. Also, suppose the service provides a query method to "get client info" that lets you know if the client ID exists. Then, it is entirely possible to avoid the exception using good coding practice. But in the real world, companies are exposing more services to end-user clients that often must hit the Web Services of external partners such as a bank or shipper, so the problem of exception handling is a real one.

Below is a real (although sanitized) SOAP exception. Notice the detail section of the exception, this is the critical issue as we will see in this article. detail is specified in the SOAP specification, and you don't own it. Everything inside detail is owned by the Web Service owner. As we shall see below, the fact that the content of detail is at the Web Service developer's discretion is why SOAP exception handling is such a chore.

//This is what a complete soap exception
//looks like when captured using fiddler2
//<ahref="http://www.fiddler2.com/">http://www.fiddler2.com</a><?xmlversion="1.0"encoding="utf-8"?><soap:Envelopexmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><soap:Fault><faultcode>soap:Client</faultcode><faultstring>System.Web.Services.Protocols.SoapException:
Provisioning violation: unknown IP address. IP address [xxxxxxx]
is not recognized or client application is not
a licensed subscriber.</faultstring><faultactor>GetCustomerProfile</faultactor><detail><faulttype>Data</faulttype><errorlist><fielderrorcode="Client"errortype="Provision">[127.0.0.1]</field></errorlist></detail></soap:Fault></soap:Body></soap:Envelope>

Many Web Service developers take a cheap shot, and return your requested object with an embedded error response in the object. Other Web Service developers take the high road and actually develop SOAP exception handling in their system. There are two sources of SOAP exceptions: those that are generated in the client, and those generated by the Web Service. You must handle both. Client side exceptions are things like endpoint problems, time-outs, network problems, things that keep you from reaching the Web Service or loosing connection to it. Web Service generated exceptions (like the one above) take three forms: Schema problems, Client data problems (application level), and Server side problems. Schema problems surface when the contracts between the Web Service (XSD) and client are no longer in sync (somebody changed something somewhere). Client data problems usually should be caught early, but sometimes they sneak through. Server side issues could be anything inside the Web Service provider's network; for example, their database could be offline. In the case of a SOAP exception generated by the server, you will get just a "server" exception (<faultcode>soap:Server</faultcode>); they aren't going to tell you why their internal system is failing, only that it has. Usually, your only option is to try again later in that case.

OK. We know that SOAP exceptions are a fact of life, what do we do?

The Web Service provided in this project is ASMX based because SOAP exception handling is not so much of an issue in WCF to WCF client/server configurations. Also, many .NET Web Service adopters have implemented ASMX solutions, and are slow to change to WCF. Naturally, there are also all the other Web Services that are not and never will be .NET based. So in your career as a developer, you will more than likely face the management of SOAP exceptions from Web Services you don't control. I have created, in this project, three possible SOAP exception generation methods that are commonly employed by Web Service providers. I have called them Simple, Multiple-element, and Complex. Since these exceptions are totally optional, there is no standard other than that given by the Web Service provider in their API documentation. These are common implementations; however, there are any possible number of combinations, which is why it not easy.

Simple SOAP Exceptions

A "Simple" SOAP exception will have the entire error response inside a single XML element, inside the detail. It could be text or additional XML data, but it is all contained within a single element. Below are a couple possibilities. This is "simple" because with WCF, you can get the first element (and all its contents) easily.

<!--Simple SoapException: All elements are contained
inside a single XML element inside the <detail>.--><detail><soapexception1><faulttype>Data</faulttype><errorlist><errorcode>123</errorcode><errorsource>methodName<errorsource><errordata>Mr. James O'brian</errordata></errorlist></soapexception1></detail>
//
<!--Simple SoapException: A text based (csv) exception also
contained inside a single XML element inside the <detail>.--><detail><soapexception1>
Data, 123, methodName, Mr. James O'brian
</soapexception1></detail>

When the SoapException (converted by WCF to FaultException) is caught, we want to get the detail if there is any detail provided by the Web Service. Assuming that the Web Service developers provide some details about the nature of the exception, we will want to get that information. We interpret it based on the API because with ASMX, we don't get classes with the exceptions that are possible in WCF.

Walking the Code

First, we create a MessageFault instance loaded with the SOAP exception that was thrown. Then, we check it to see if the Web Service has provided any detail information. If it does, get it next. In this example, I am expecting a text string representation of our exception data. I am putting the fault data into an XmlDocument in case the return is not text but more XML, in which case, we could easily navigate the data. So, with a text response, we just have a small expense of an XmlDocument container. The CreateNode method creates for us a "root" element which is named the same as SoapException.DetailElementName.Name that was created in the SoapException. We are going to append our fault as a child of the node. By the way, this creates a proper XML document. I have found it easier to work with XML using LINQ, so I am using a helper method and am putting the node information into an XElement. Next, I use the same method to get the actual fault detail, also into an XElement. I append that to the node. Next, I put all of it back into the node (our XmlDocument). You could now use standard XmlDocument navigation to decode the data. However, since this is a simple exception, all of it is readily available at this point, as shown in the code above.

Multiple Element SOAP Exceptions

More difficult to extract, at least with WCF, is a SOAP exception that contains multiple discrete XML elements. Why? Remember our messageFault.GetDetail() above, it will only return the first detail item. Everything else is out of reach of the messageFault object, because Microsoft did not provide a "detail" property in the FaultException class. In the sample XML data below, we get only the first element with the messageFault.GetDetail() method. There must be a way, and naturally there is, see code below.

Walking the Code

In this sample, since our Web Service API specifies multiple simple XML elements (no attributes), we are reading each element in turn into a dictionary, storing the element tag name and the value in the element. The NodeType test filters out XML end tags such as "</errorlist>". The ReadEndElement() is required to actually "read" the line in to advance the reader forward to the next element to read. When the EOF is encountered, the reader exits. Once we exit the reader, we have our API implementation of our exception handler to actually do something meaningful.

Complex SOAP Exceptions

Never said exception handling was easy! It gets worse! Next, we will look at what I call a Complex SOAP Exception. It contains multiple elements, with nested elements that have attributes on the elements. Jump back up to the top of this article, and take another look at the real SOAP exception.

<!--Complex soap exception with multiple
and nested elements with attributes.--><detail><faulttype>Data</faulttype><errorlist><fielderrorcode="Client"errortype="Provision">[127.0.0.1]</field></errorlist></detail>

Wow! This is actually smaller than the multiple element example. It is because the Web Service provider API specifies XML data detail. That means that we simply drop the XML into an XmlDocument object. Then, your job is to use standard XML methods to manage the data. If by chance the data is text, then you can do something like I have in the example above and make a string out of it. You could then parse the string into meaningful data.

Points of Interest

I won't tell you how many days it took to find this solution. I also know that lots of developers do a very bad job of exception handling. More of my clients use ASMX and SOAP exceptions than WCF. As a software architect, I see lots of solutions, good and bad. My experience is that development projects are driven by the lowest bid, or by internal demands that don't foster best practices. Companies lose tons of money on projects, that were rushed to production, when the maintenance begins. Companies also don't have policies of on-going code reviews to look for problems. As an outside contractor, I most often come in when the existing systems have consumed all the existing IT staff's time and they are on the brink of collapse.

I hope this will help you. My thanks again to John Saunders for his contribution. You can see the forum thread here.

Share

About the Author

My company specializes Microsoft Windows Azure Cloud Computing in private and public sector consulting. We have a number of highly visible successful cloud projects including Florida Voter Registration System for the elections in 2012.
Intact develops not just the cloud solution, we develop your organization. We know that the leap to cloud is a jump that most are unable to make. At the same time the demands for mobile devices and applications drain all the energy. Intact has successfully helped large business and government over the hump. We can help you too.
Visit us at: http://www.itintact.com

Comments and Discussions

iam using c# and Is it possible to use ExceptionShielding with asyncpattern =true because we not able to get the fault exception at client when there is some exception in service code and says that exception was unhandled but its working fine when we did not use asyncpattern =true.

I'm having the same problem I needed to call a soap webservice from a wcf client (powerBuilder .Net), and my try-catch falls into a SYSTEM.REFLECTION.TARGETINVOCATIONEXCEPTION, not in the System.ServiceModel.FaultException.

Larry, great article, but like speedcat, I can't use it because WCF is not treating the SOAP fault message as a FaultException.

I think I have the same problem as speedcat. I'm hitting a customer's non-WCF SOAP 1.2 web service from my WCF client. All is well as long as no SOAP fault is generated. When it is, say for failing a validation test performed by the service application, I get the following exception instead of a FaultException:
[System.ServiceModel.ProtocolException] {"The remote server returned an unexpected response: (400) Processed."} System.ServiceModel.ProtocolException

When I try the same request from SoapUI, I get a well formed SOAP fault envelope back with a nice detail section explaining that one of my elements was malformed.

How can I grab this same detail from within WCF?

Thanks and best regards,
Bob

p.s. I tried the IClientMessageInspector method, but unfortunately, the Protocol Exception is thrown before the AfterReceiveReply function can be called. Consequently, I have no chance to examine the response message. I have access only to "happy" responses.

I have posted on the MSDN forums about this issue. A reader sent me a response about the problem of getting soap exceptions in WCF clients. I appreciate that and continue to press for better documentation. It has been more than a week at this point since my team encountered this problem. That means we had a team member devoted to this issue for a week! That sets our project back and costs REAL dollars. If I were paying a consultant this issue alone had at least a $5000 dollar price tag not to mention a week loss. So I submit that documentation on every level (not just the framework developers) is as important as the code itself.

This happens to be in the "System.ServiceModel.Dispatcher Namespace". I have used Microsoft stuff for years and am and old C and C++ guy. A "dispatcher" is not exactly the first place I would look for exception handling code.

Next when I do go there I find this as the overview: "Contains the classes, enumerations, and interfaces necessary to modify the runtime execution behavior of service and client applications."

Well that clears it right up - wow! Thank you documentation writer.

I mean - come on... I know all of us stay up late at night with our laptops doing light reading of MSDN documentation.

And it is not just Microsoft! This is an industry wide prevasive problem. Some of the worst documentation I have ever seen comes from large component vendors. Open-Source you're no better.

This will not change until we (developers make it an issue - a real $$ based problem.) Coders like to find stuff on the web and include it in their code - great everybody does, document the link so two years from now some poor project manager will be able to do his job too. Everybody is guilty - everybody.

Larry, thanks so much for a most excellent article. Have not found anything so helpful and clear on this topic anywhere.

Meanwhile I'm having a problem - my WCF client that talks to a non-WCF service seems to be seeing all faults returned as CommunicationException, and I cannot get them to be regarded as FaultException. So I can't use the methods you've described in your article to get the inner messages I need. I know for a fact there is a good SOAP fault message response for the very same error condition as I tested this using SoapUI).