Introduction

My main project at work involves a fairly large application using Windows Forms as the front end, communicating via Web Services with business logic and data access code on the server. I’ve found it very troublesome to track down the cause of unhandled exceptions on the server side, because of the limited exception information you get back in the SOAP message, so I have created a SOAP extension to provide more information.

How it Works

This is a fairly simple piece of code, the SOAP extension listens to every message which is passed between the two machines. If an exception is detected in a response about to be sent back to the client, the extension notices this, and gets the full information about the exception, which it inserts into the XML stream heading back for the client.

The client, after receiving the SOAP response, will notice that an exception is present in the stream, and again the extension will step in to retrieve the additional information which we inserted into the stream on the server. Once it has this, it throws a new exception containing that extended information.

The ProcessMessage method is at the heart of any SOAP extension, it gets called before and after (de)serialization on the server and the client for both the request and the response. Its job is to copy information between the input and output streams at the correct points, and optionally step in and examine or manipulate the SOAP message. Our implementation of it looks like this:

The other significant methods in this class are called InsertExceptionDetails and GetExceptionDetails. They are responsible for the task of inserting the extended exception information into the stream, and retrieving it. The extended information is inserted into an XML element called ExtendedExceptionDetails within the standard soap:Fault element.

Using the code

To use the code, add references to the ExceptionHandlingSoapExtension assembly, in both the client and web service projects. Add the following section to both App.Config (on the client) and Web.Config (on the server):

This code instructs ASP.NET on both the client and server to use this SOAP extension to process messages.

With the SOAP extension set up, every time an exception occurs during the execution of a web method, an exception will be thrown on the client which will have the message field populated with a full stack trace showing what happened on the server.

Screenshot showing the standard information returned from a web method exception.

Screenshot showing the extended information returned courtesy of the SOAP extension.

Real-World Usage

Security

It would be quite a security risk to have publicly accessible web services set up like this; exposing information about the internal workings of your web services can be very useful to malicious users, and a full stack trace exposes a lot! We use this extension in conjunction with various encryption and authentication systems, and the web services are not made accessible on the public Internet so there is little danger of this information falling into the wrong hands.

Cross-platform usage

I believe that non .NET clients consuming web services with this extension will simply ignore the additional exception information, however I cannot guarantee this. In my case, the web services we create will never be accessed by clients other than those we develop so I have not investigated this.

Conclusion

I think it's debatable whether Web Services are the right choice for the type of application I'm developing, but the choice was made well before I started work on the project.

Learning how to write a SOAP extension was interesting for me, I found the MSDN documentation a little misleading to say the least, but they are certainly a simple and powerful way to manipulate web service messages.

I hope this article is helpful to somebody, and I would welcome any feedback - I know I still have a lot to learn!

History

06/06/2005: Created.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

I have written such an extension just a few months ago. It works pretty good, but you do need to supply a little bit more to make it more efficient and easier to use.

You always use your own stream to copy the data, which is pretty inefficient for large messages (i.e. datasets). It is much more efficient to only switch streams when an exception occurs. It avoids unnecessary copying and memory resources when not needed.

I have added an initializer for the SOAP extension attribute that (when set) filters out the inner exception. This is often convenient for applications that should not expose its inner workings.

Before I say anything, I have to say I was intrigued by this. It is, after all, a new and different use for a SoapExtension. So don't take what I ask here as criticism...this is truly an honest question.

Whenever ASP.NET catches an unhandled exception when processing a Web Service request, it automatically formats that into a SoapException for transmission to the client. The returned information does contain some useful clues but is often lacking (hence this article). My question would be, why have unhandled exceptions, and if we can handle them more efficiently, why not do that instead? I say "efficiently" because you have to root through the XML to find an error and then decide what to say about that exception rather than handling it at the point of origin.

Then the catch blocks would prevent unhandled exceptions (at least as usually defined...not much you can do if ASP.NET croaks). Moreover, you can insert nearly anything in your own self-generated SoapException, including (and I do this a lot) your own SOAP Fault detail element. Use the constructor that accepts an XmlNode. And having differing exception blocks allows for easier type filtering and tailoring of the resulting SoapException (i.e.: the separate blocks for SqlException and Exception...insert your own exception handler[s] as necessary).

I've also noted that a great many corporations are using Web Services internally where this information is truly useful. Far fewer public services are offered, although they do exist. For those, I'd filter the error accordingly.

The one argument I do see that a SoapException would offer in this case would be that we could configure the SoapException in the Web.config file and totally change the way exceptions are reported with a simple change-out of the extension and a slight modification to the configuration file. Hard-coding the exceptions as I've done here may not be appropriate for all situations.

Our exception handling strategy, in line with Microsoft's guidelines, is that we only catch those specific exceptions which we can deal with and continue without interruption or loss of data, or those which need clean up afterwards such as closing DB connections, files etc... (of course in those cases we use a finally block rather than catching exceptions). Any exceptions which are unexpected get propagated up the call stack to a global exception handler which gathers information and logs details of the exception for later troubleshooting.

Web services confuse the issue a bit, most people would recommend extra exception handling code around remoting boundaries, but we decided that if an unexpected exception was thrown on the server side, the most useful thing to happen would be if it propagated all the way up to the client and got logged there.

The specifics of our project make the SOAP extension very suitable - the client application and the web services are quite tightly coupled, and the web services will only ever be consumed by client code we write ourselves. Your example of throwing SoapExceptions would make much more sense if the web services were less tightly coupled or if they were to be made public at some stage.