SoapException sucks.

Security Briefs

Syndication

I've been looking for a simple way to communicate exception information from a web service to its clients. The SOAP spec defines a reasonable way to do this, but the programming model (SoapException) really makes it painful. Here's what I'm trying to model:

try {
  int value = LookupSomething("some index");
}
catch (ItemNotFoundException) {
  // certain special exceptions I can recover from
}

This is a very common programming construct - in certain cases we can easily recover from an exception, so we create special exception classes (if they don't already exist) to signal these situations. In a web service, you might use the faultstring to signal these sorts of things (the faultcode is controlled by the SOAP spec, so that's not something I want to customize). Well, to do this, you use SoapException, and you can set the SOAP faultstring via the Message property:

throw new SoapException("ItemNotFound", ...);

and on the client, you'd expect to be able to read the faultstring:

catch (SoapException x) {
  if ("ItemNotFound" == x.FaultString)
       recover();
  else throw;
}

Here's the problem: there is no FaultString property on SoapException. And if you read the Message property, you'll find your faultstring interspersed with a load of other crap that you don't care about:

System.Web.Services.Protocols.SoapException: ItemNotFound at Foo.LookupSomething(String index) in foo.cs:line 80

Well, that's just peachy. So I guess I can use the detail field to supply a DOM with details about the exception, and the client SoapException won't futz around with that, but my goodness, that's way overkill for what I want.

Am I missing something, or did Microsoft simply not consider the client exception programming model? Did they just assume we'd implement all our exception handling blocks with a message box?


Posted Jun 02 2005, 03:01 PM by keith-brown
Filed under:

Comments

Haacked wrote re: SoapException sucks.
on 06-02-2005 3:39 PM
Have you tried grabbing the .InnerException to look at the original exception thrown?
Keith Brown wrote re: SoapException sucks.
on 06-02-2005 4:58 PM
ASP.NET doesn't serialize the actual exception into the SOAP fault packet, so InnerException is always null on the client side. I should have mentioned that in my original post, because it was one thing I thought about.
Matt MIlner wrote re: SoapException sucks.
on 06-02-2005 6:05 PM
It is not intuitive, but you need to set the custom errors mode in your web.config to On, not remote only or Off. This controls how your web service errors are serialized as well as how errors are handled in asp.net apps. you'll actually get the exception message you want and if you have an inner exception, you'll get that message in the message as well (OuterMessage -> inner exception message).

This is the kind of great detail you can get if you have us come in and do a web services class for your company. :)
Sahil Malik wrote re: SoapException sucks.
on 06-02-2005 6:07 PM
Keith,

One thing you can do is, supply just the XML as a string, and it works in a way very similar to supplying the DOM document (hey afterall it is all XML Serialized).

Not the ideal solution - but close enough.

- SM
Keith Brown wrote re: SoapException sucks.
on 06-02-2005 6:13 PM
Matt,

Good point, but would it be a good idea from a security perspective to change that error mode? Heck, I'd be perfectly happy with just a FaultString property on SoapException. Seems like an oversight to me.

Hey, I'll sit through your web service class if you'll sit through my security class ;-)

Sahil,

I solved the problem by creating a little class that maps enums onto nodes in a DOM tree that it caches. Makes it pretty easy to use the Detail field. Just silly that we have to go to this length, IMHO.
Nicole Calinoiu wrote re: SoapException sucks.
on 06-03-2005 4:33 AM
I've been using a rather strict server-side exception mapping approach to avoid the security issues that stem from this problem, but I suspect that this approach might address your client-side issue as well. Here's the basic outline:

1. Define an marker attribute for client-consumable exceptions.

2. Create two new exception classes that inherit from SoapException and SoapHeaderException but that hide all the innards (e.g.: stack trace) that shouldn't be exposed to the client. Apply the attribute from #1 to both classes.

3. Create an exception mapper that is capable of mapping any server-side exception that isn't marked with the client-safe attribute to one of the exception types in #2, including massaging the message text as appropriate.

4. Wrap each web method body in a try-catch block that uses the method from #3 to map caught exceptions to their client-safe counterparts then rethrows the new exception.

Since SoapExceptions aren't re-massaged by the framework prior to insertion of their data into the response, whatever you choose to have exposed via the ToString method of the custom exception classes created in #2 will be what is sent to the client.
Aaron Robinson wrote re: SoapException sucks.
on 06-03-2005 5:56 AM
This details the approach we took for passing exceptions on the wire:

<http://www.microsoft.com/belux/fr/msdn/community/columns/jtielens/soapexceptions.mspx>
Keith Brown wrote re: SoapException sucks.
on 06-03-2005 8:40 AM
Interesting approach, Nicole. I ended up doing something very similar (but a bit simpler) to Aaron's suggestion.
Chris Dickson wrote re: SoapException sucks.
on 06-03-2005 9:09 AM
The SOAP spec says...
faultstring: "...It is intended to provide a human readable explanation of the fault and is not intended for algorithmic processing."
detail: "The detail element information item is intended for carrying application specific error information related to the SOAP Body . It MUST be present when the contents of the SOAP Body could not be processed successfully"

Interoperable SOAP services therefore more or less require you to follow the approach of providing a detail element complying with a schema defined as part of your service. WSDL has a syntax for defining the schema of detail elements in SOAP faults. However, I think you are right that there is currently no tool support from Microsoft for doing this: no way to emit the WSDL including fault definitions (unless you do it by hand); no server-side programming constructs for ensuring that any SOAP faults generated comply with the detail schema; and no client-side tools for generating code to map fault detail instances to specific client side exception types.

Currently you are more or less forced to catch all exceptions on the server side and construct a suitable SoapException "by hand" (setting faultcode appropriately; ensuring the faultstring doesn't leak sensitive information; and populating the detail appropriately), and on the client side you need to code an explicit mapping of SOAP fault context details to whatever client-side exception handling model your application has.

Such mappings are necessarily application-specific. While wrapper classes such as have been suggested can help, unless they are closely configurable for the application, they can't solve the whole problem. For example, even just to get the faultcode set correctly on the server side - to distinguish server from client errors - application-specific coding is hard to avoid.
Keith Brown wrote re: SoapException sucks.
on 06-03-2005 10:12 AM
Chris,

Good point about the SOAP spec. Given that, it appears that not exposing the faultstring was probably a reasonable thing to do. So I guess it sucks a little less than I originally thought ;-)

Indigo has a much more robust way of dealing with faults. You can throw an exception on the server and catch it on the client. Indigo also differentiates between faults that should be thrown to clients and random exceptions that should be dealt with on the server. Looking forward to it...
Sean Chase wrote re: SoapException sucks.
on 06-03-2005 1:21 PM
Great conversation. I've been looking for a good solution to this problem for some time now. Sounds like a great topic for an MSDN article!
Haacked wrote re: SoapException sucks.
on 06-04-2005 9:28 AM
You don't have to catch all exceptions by hand on the server side. You can build a SoapExtension that will inject the exception information into the detail node of the outgoing stream.

I've built an extension that does that. It even serializes the exception (base64encoded) and passes that along with human readable information. Not sure if the serialized exception is useful as I already pass the exception type and message.

I'll probably publish it on my blog sometime soon.
Dave Angers wrote re: SoapException sucks.
on 06-04-2005 3:23 PM
Am I missing something here?

The soap spec (1.1) also includes:
faultcode - intended for use by software to provide an algorithmic mechanism for indetifying the fault.
(for 1.2 see Value element of Code element)

I see the faultcode as the rough equivalent to an exception name while "detail" is the exception contents.

There are some predefined faultcodes - soap:Client, soap:Sender for instance - but you can define your own:
xmlns:my = "http://my.com"
faultcode = "my:ItemNotFound"
(see XmlQualifiedName)
and these can be passed as the code parameter to various SoapException ctors.

See some of the WS specs - like Web services Eventing - for examples of custom faultcodes.
laker2000 wrote re: SoapException sucks.
on 06-13-2005 11:59 AM
So Keith, what did your solution ended looking like?
Greg Robinson wrote re: SoapException sucks.
on 06-15-2005 7:02 AM
I too have struggled with this. Our web service is used by ASP, ASP.NET, Perl and ColFusion clients. They all are unhappy with how we report 'errors'. I use to throw a soapexception, now though I return a string with a meaningful message.
you've been HAACKED wrote Exception Injection Using a Custom SOAP Extension
on 06-29-2005 11:01 AM
Haacked wrote re: SoapException sucks.
on 06-29-2005 6:24 PM
Ok, I finally blogged about my soap extension to handle injecting the information you're looking for. Let me know if this is useful or not.
simon.says wrote Custom Exception Handling in Indigo
on 07-12-2005 1:13 PM
One question I frequently get asked is "how do I handle exceptions over Web Services?"&nbsp; If you've...
gobalistik wrote re: SoapException sucks.
on 07-14-2005 10:40 AM
Did everyone miss Matt MIlner's comment?

Cheers Matt. After searching for the last 2 hours, setting CustomErros to On in the Web.config of my web service now produces exception messages as they're thrown, not with the bollocks wrapped around them.
Mathew Nolton wrote re: SoapException sucks.
on 08-08-2005 2:14 PM
It is painful. I posted a bit of code a while back on an approach for handling it. Basically, i use custom attributes and an exception factory on the web service side to fill in the customer section.

http://weblogs.asp.net/mnolton/archive/2003/09/17/28015.aspx

At the end of the day I agree with some of the folks that you should not turn on inner exceptions. Internally, why your service fails is not something you really want to expose. At the same time, typed exceptions are needed on the client (besides just SoapException or SoapHeaderException). Personally, I think that exception typing should be part of the web service contract.

-Mathew Nolton
you've been HAACKED wrote Exception Injection Using a Custom SOAP Extension
on 11-02-2005 10:49 AM
you've been HAACKED wrote Exception Injection Using a Custom SOAP Extension
on 11-02-2005 10:50 AM
Jeff Atwood wrote re: SoapException sucks.
on 11-02-2005 11:08 AM
Here's a technique for injecting better SOAP exception information into the message:

http://www.codinghorror.com/blog/archives/000054.html
Jeff Atwood wrote re: SoapException sucks.
on 11-02-2005 11:10 AM
Er, sorry, here's the SoapExtension:

http://www.codinghorror.com/blog/archives/000108.html
Craig wrote re: SoapException sucks.
on 01-03-2006 11:16 AM
Have you tried using

<customErrors mode="On" />

?

See http://pluralsight.com/blogs/craig/archive/2005/11/02/16155.aspx for why it might make a difference.
C# .Net Tales wrote Web Service Exception Handling
on 10-17-2006 9:24 PM
I&#39;ve been using the new Web Services Factory and am looking into the arguments behind Exception Shielding
Ian wrote re: SoapException sucks.
on 10-26-2006 11:24 AM
My application uses web services over remoting, and the details I add in the xmlNode of the SoapException do not show on the client side.
I resigned myself into sending all error info in one string.
Anyone has an idea of what may be wrong?
Thanks.

Jason Bowers wrote re: SoapException sucks.
on 10-31-2006 12:56 PM
Does this get more to the point? It gives back the strong typing to soap exceptions and allows the developer to still use the try / catch construct.



using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Security.Authentication;
using System.Security;
using System.Text.RegularExpressions;

namespace exceptionClient
{
class Program
{
static void Main(string[] args)
{
MyClass c = new MyClass();

try
{
c.CallThatMayFail("Bad Param");
}
catch (System.Web.Services.Protocols.SoapException soapex)
{
try
{
ExceptionAdapter.Adapt(soapex);
}
catch (System.Security.Authentication.InvalidCredentialException icex)
{
Console.WriteLine(icex.Message);
}
}

Console.Read();
}
}

class ExceptionAdapter
{
public static void Adapt(System.Web.Services.Protocols.SoapException ex)
{
MatchCollection mc = Regex.Matches(ex.Message, "---> ([^:]+):");

if(mc.Count >= 1)
{
if (mc[0].Groups.Count > 1)
{
if (mc[0].Groups[1].Value + "" != string.Empty)
{
Type t = null;

foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
{
t = asm.GetType(mc[0].Groups[1].Value);
if (t != null)
break;
}

if (t != null)
{
MatchCollection mcMsg = Regex.Matches(ex.Message,
@"--->[^:]+\:([^\n]+)\n");

object oex = null;

if (mcMsg.Count >= 1)
{
if (mcMsg[0].Groups.Count > 1)
{
oex = System.Activator.CreateInstance(
t, new object[] { mcMsg[0].Groups[1].Value.Trim() });

throw (System.Exception)oex;
}
}

oex = System.Activator.CreateInstance(t);
throw (System.Exception)oex;

}
}
}
}
}
}
}