Eric's Blog

Day to day experience in .NET
Welcome to Blogs @ IRM Sign in | Join | Help
 Search

Disclaimer

The content of this site is my own personal opinion and does not in any way represent my employer, it's subsideries or affiliates. These postings are provided "AS IS" with no warranties, and confer no rights.

This Blog

Write a custom ExceptionHandler to expose more details in SoapException

A couple of years ago I wrote a SoapExtension that inserted a serialized version of the real exception (not a SoapException) into the fault element in a Soap message. The purpose was to use it when using web services to communicate between a client and server on an intranet instead of .NET Remoting.
This solution has several drawbacks; for example requiring that the client is using the same SoapExtension to deserialize the exception (and it must therefore be a .NET client) and not working with WSE. I have now written an custom exception handler to use with the new Exception Handler Application Block (EHAB) with the same purpose (that is getting the real exception to the client). I will do this in a two posts and this first one is about the server side. Lets start with some basic requirements:
 
///
/// Replaces the exception in the chain of handlers with a .
///
///
/// The SoapException will contain the original exception serialized in the
/// property.
///
/// All stack information will be removed from the serialized exception.
///
///
public class SoapServerExceptionHandler : ExceptionHandler
{
///
/// Initializes a new instance of the class.
///
public SoapServerExceptionHandler()
{
}
 
///
/// Initializes the provider with an .
///
/// The ExceptionHandlingConfigurationView object.
public override void Initialize(ConfigurationView configurationView)
{
return;
}
}
 
The SoapServerExceptionHandler inherits Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ExceptionHandler and is required to override Initialize (we aren't interested because it's not possible to do any special configuration of this exception handler) as well as HandleException which is the core method:
 
///
/// Replaces the exception with a .
///
/// The original exception.
/// The name of the
/// The unique ID attached to the handling chain for this handling instance.
/// A to pass to the next handler in the chain.
public override Exception HandleException(Exception exception, string policyName, Guid handlingInstanceId)
{
SoapException soapEx = exception as SoapException;
 
if (soapEx == null)
{
XmlNode detail = this.SerializeException(exception);
string actor = System.Web.HttpContext.Current.Request.Url.ToString();
XmlQualifiedName qualifiedName;
if (exception is IRM.BusinessRuleException || exception is ArgumentException)
qualifiedName = SoapException.ClientFaultCode;
else
qualifiedName = SoapException.ServerFaultCode;
 
soapEx = new SoapException(exception.Message, qualifiedName, actor, detail, exception);
}
 
return soapEx;
}
 
An exception thrown from a web services is always replaced with a SoapException by the framework because of security requirements. With this solution I am creating the SoapException myself to be able to have more control over the SoapException. I shall point out clear that this solution is weaker in terms of security because the detail of the SoapException will include the real exception object serialized. I have also made an assumption that whenever the exception is of type ArgumentException or BusinessRuleException (a custom exception that I have written that extends ApplicationException with a DataSet property) then the message send to the web services has errors in it and because of that it is the clients fault. The message of the SoapException is set to the same message as the original exception which is the same behavior that the framework uses. Lets look a little bit closere at the method SerializeException that returns a XmlNode to be used as the detail.
 
private XmlElement SerializeException(Exception exception)
{
System.IO.MemoryStream stream = new System.IO.MemoryStream();
SoapFormatter formatter = new SoapFormatter();
System.IO.TextReader reader = null;
 
try
{
formatter.Serialize(stream, exception);
stream.Position = 0;
 
XmlDocument dom = new XmlDocument();
dom.AppendChild(dom.CreateElement("detail"));
 
reader = new System.IO.StreamReader(stream);
dom.DocumentElement.InnerXml = reader.ReadToEnd();
 
//Make the serialized exception safer
//Remove all StackTrace information
XmlNamespaceManager nsmgr = new XmlNamespaceManager(dom.NameTable);
this.ClearElementContent(dom.DocumentElement, "//StackTraceString", nsmgr);
this.ClearElementContent(dom.DocumentElement, "//RemoteStackTraceString", nsmgr);
this.ClearElementContent(dom.DocumentElement, "//ExceptionMethod", nsmgr);
 
return dom.DocumentElement;
}
finally
{
if (reader != null)
reader.Close();
 
((IDisposable)stream).Dispose();
}
}
 
private void ClearElementContent(XmlElement el, string xpath, XmlNamespaceManager nsmgr)
{
XmlNodeList nodes = el.SelectNodes(xpath, nsmgr);
if (nodes != null)
foreach (XmlNode node in nodes)
node.InnerXml = "";
}
 
This method uses the SoapFormatter to serialize the exception and not the XmlSerializer (which is the one used by the framework for web services) because it will throw security exceptions for all excpetions except for SoapException. The serialized exception is placed in the detail element which also is the element that later is returned from the method. To become a little bit more secure I also removes all information about the stack trace and about the method causing the exception.
The described approach works great together with WSE and also has a greater reach since it is only the detail element that contains the serialized exception (which is valid Xml that any client could parse to get information from). I haven't verified this solution with any Java written client, but I guess that it should work just fine.
The use is very easy. Start with configuring a policy and which exception types you want to be handled this way (maybe just a few if you expose your service externally) and in the code I place a catch block in each web service method like this:
 
catch(Exception e)
{
bool rethrow = ExceptionPolicy.HandleException(e, "Soap Policy");
 
if (rethrow)
throw;
}
 
Next time I will post about how to handle the SoapException on the client side.
Published den 27 februari 2005 18:53 by ericqu
Filed under: ,

Comments

 

C# .Net Tales said:

I've been using the new Web Services Factory and am looking into the arguments behind Exception Shielding

oktober 18, 2006 04:27
New Comments to this post are disabled
Powered by Community Server, by Telligent Systems