[Download Sample Code]
Overview
A Web service provides functionality independent of the programming languages and the operating system; hence, it’s necessary for the Web services to throw exceptions in a platform-independent manner. To accomplish this, we need to raise SOAP exceptions from the Web service that are compliant with the SOAP specification.
The following are the advantages of SOAP exceptions.
- They handle exceptions in a consistent manner.
- They follow the SOAP specification.
- Explicitly raising SOAP exceptions communicates more information, such as detailed information of the exception, Web Service method name, and responsibility.
Now that we have had a look at the importance of raising SOAP exceptions, let us look at how to raise exceptions from Web services.
Raising Exceptions from Web Services
To accomplish this, we should use the SoapException class as it abstracts the complexities of the SOAP fault creation process. The SoapException class consists of the following properties that need to be populated before throwing the exception to the consumers.
- Message—the contents of the exception.
- Code—an enumeration that specifies the type of fault code (e.g. ClientFaultCode, ServerFaultCode, etc.).
- Actor—the URL of the Web service method where the exception has occurred.
- Detail—the detail element can be used to communicate more information about the exception to the callers.
For the purposes of this example, we’ll create a Web service named WSException, selecting a C# ASP.NET Web Service as the project template. Once the project is created, add a service called WSException and add a method named GetException and the following lines of code to that class.
Listing 1
public enum FaultCode
{
Client = 0,
Server = 1
}
[WebMethod]
public void GetException()
{
try
{
int i = 1;
int j = 0;
int z;
//generates the exception
z = i / j;
}
catch (Exception ex)
{
throw RaiseException("GetException", "WSSoapException", ex.Message,
"1000", ex.Source, FaultCode.Server);
}
}
//Creates the SoapException object with all the error details
public SoapException RaiseException(string uri, string webServiceNamespace,
string errorMessage,
string errorNumber,
string errorSource,
FaultCode code)
{
XmlQualifiedName faultCodeLocation = null;
//Identify the location of the FaultCode
switch (code)
{
case FaultCode.Client:
faultCodeLocation = SoapException.ClientFaultCode;
break;
case FaultCode.Server:
faultCodeLocation = SoapException.ServerFaultCode;
break;
}
XmlDocument xmlDoc = new XmlDocument();
//Create the Detail node
XmlNode rootNode = xmlDoc.CreateNode(XmlNodeType.Element,
SoapException.DetailElementName.Name,
SoapException.DetailElementName.Namespace);
//Build specific details for the SoapException
//Add first child of detail XML element.
XmlNode errorNode = xmlDoc.CreateNode(XmlNodeType.Element, "Error",
webServiceNamespace);
//Create and set the value for the ErrorNumber node
XmlNode errorNumberNode =
xmlDoc.CreateNode(XmlNodeType.Element, "ErrorNumber",
webServiceNamespace);
errorNumberNode.InnerText = errorNumber;
//Create and set the value for the ErrorMessage node
XmlNode errorMessageNode = xmlDoc.CreateNode(XmlNodeType.Element,
"ErrorMessage",
webServiceNamespace);
errorMessageNode.InnerText = errorMessage;
//Create and set the value for the ErrorSource node
XmlNode errorSourceNode =
xmlDoc.CreateNode(XmlNodeType.Element, "ErrorSource",
webServiceNamespace);
errorSourceNode.InnerText = errorSource;
//Append the Error child element nodes to the root detail node.
errorNode.AppendChild(errorNumberNode);
errorNode.AppendChild(errorMessageNode);
errorNode.AppendChild(errorSourceNode);
//Append the Detail node to the root node
rootNode.AppendChild(errorNode);
//Construct the exception
SoapException soapEx = new SoapException(errorMessage,
faultCodeLocation, uri,
rootNode);
//Raise the exception back to the caller
return soapEx;
}
In the above listing, as the name suggests, the GetException Web method throws the exception (as we are dividing 1 by 0 to generate the exception) when it is called from the client side. This exception is converted into the SoapException object by the RaiseException method which is called in the catch block of the GetException Web method.
In the above code, the RaiseException method is used to raise exceptions from the Web service in the form of a SoapException object. The RaiseException method first looks at the FaultCode enum parameter value to find out the source of the exception. If an exception has occurred due to problems in the server side (for example, the file is not found), it sets the value of FaultCode to SoapException.ServerFaultCode.
Similarly, if an exception has occurred due to a problem in the input parameter passed by the client to the Web method, then FaultCode is set to SoapException.ClientFaultCode. Once it is done, it creates an XmlDocument object to hold the contents of the detail element. It adds all the child elements under the detail element and then passes the detail node to the constructor of the SoapException object. Finally, it returns the SoapException object back to the caller by using the return statement. Detail element inside the SoapException object will look as follows.
Listing 2
<detail>
<Error xmlns=" http://tempuri.org/WSException /">
<ErrorNumber>1000</ErrorNumber>
<ErrorMessage>Exception Message</ErrorMessage>
<ErrorSource>Exception Source</ErrorSource>
</Error>
</detail>
When the client application receives a SoapException from the Web service, it looks at the Detail property of the SoapException object to get more information about the generated exception.
Handling Exceptions at the Client Side
This section provides an example to handle the exceptions raised from the Web service on the client side. To demonstrate this, let us create a new GUI project named SoapException. Once the project is created, add a command button to the default form and name it RaiseException. Add a Web Reference to the WSException project (see Listing 1). Then modify the Click event of the command button to look like the following.
Listing 3
public void RaiseException_Click(object sender,
System.EventArgs e)
{
try
{
WSException WSEx = new WSException();
WSEx.GetException();
}
catch (SoapException soapEx)
{
MessageBox.Show(soapEx.Code.ToString());
//Load the Detail element of the SoapException object
XmlDocument doc = new XmlDocument();
doc.LoadXml(soapEx.Detail.OuterXml);
XmlNamespaceManager nsManager = new
XmlNamespaceManager(doc.NameTable);
// Add the namespace to the NamespaceManager
nsManager.AddNamespace("errorNS",
"WSSoapException");
XmlNode Node =
doc.DocumentElement.SelectSingleNode("errorNS:Error",
nsManager);
string errorNumber =
Node.SelectSingleNode("errorNS:ErrorNumber",
nsManager).InnerText;
string errorMessage =
Node.SelectSingleNode("errorNS:ErrorMessage",
nsManager).InnerText;
string errorSource =
Node.SelectSingleNode("errorNS:ErrorSource",
nsManager).InnerText;
MessageBox.Show("Error Number is " + errorNumber);
MessageBox.Show("Error Message is " + errorMessage);
MessageBox.Show("Error Source is " + errorSource);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Conclusion
In this article, we have demonstrated how to raise SOAP exceptions back to the client applications by using the SoapException object. We have also seen how the SoapException object allows us to communicate the exceptions using the SOAP fault code defined in the SOAP specification. Although the application we created was simple in functionality, it should provide a solid foundation for understanding how to raise and handle exceptions from a Web service.