Home > Articles > Programming > Windows Programming

This chapter is from the book

As with all SOAP messages, the content of the message is included in the body and is processed on the server. In this case, the server is a Web service method. The body element contains the information necessary to invoke a Web service and will also be used to return any values upon successful completion of the business function. However, if any error were to occur, the body can also be used to return errant information in the form of SOAP Faults. The SOAP Fault element is used to carry this error and/or status information back to the calling client from within the SOAP message. If an error occurs, the SOAP Fault element should appear just like any other body element entry and should not appear more than once.

Remember that the SOAP message can be parsed like any other XML document. To determine whether an error occurs, the calling client can simply look for this fault element and read its contents to determine the exact error. Fortunately, the client from the SOAP Toolkit 2.0 provides users with a user-friendly component by which to query for this information. The SOAP client COM object simply wraps the nested contents of the fault, allowing access to the following elements:

faultcode

This element is used to determine the basic error identification and to provide an algorithmic mechanism for identifying the fault. This must be present in a SOAP Fault element, and it must be a qualified name and not the typical numbering schema used in other error determinations. The namespace identifier for these faultcode values is http://schemas.xmlsoap.org/soap/envelope/. The following SOAP faultcodes can be used:

  • VersionMismatch— Used for an invalid namespace in the SOAP Envelope element.

  • MustUnderstand— An element of the SOAP Header that was missing or invalid. See the SOAP Header technology backgrounder in Chapter 7 for more information.

  • Client— Used to indicate that the message was incorrectly passed and/or did not contain the right information used for the called function. This simply means it was the client's “fault” and should be retried. Retrying or repassing the information will not result in the proper processing on the server unless the contents of the passed message are changed. This is not the server's error. You set this when the caller gives the service improper information and not when something goes wrong on the server side.

  • Server— This is used to communicate a server failure (this can mean any failure to process the request from the server's perspective). Retrying or repassing the information may result in the successful completion of the function.

faultstring

This element is used to pass to the caller a descriptive human-readable error. It must be present in a SOAP Fault element and should provide at least some information explaining the nature of the fault. This could contain the high-level error message used to determine generally what went wrong.

faultactor

This element is used to provide information about who caused the fault and usually contains the Uniform Resource Identifier (URI) of the perpetrator. This is similar to the SOAP actor attribute in the SOAP body. Applications that do not act as the ultimate destination of the SOAP message must include this element. The final destination of a message may use this element to indicate that it alone threw the exception.

detail

This is where the most descriptive error information can be included to help determine the root cause of the problem. The detail element can be used for carrying application-specific error information related to the Web service method invoked. It should not contain SOAP header-related errors but only errors related to the message body. The absence of this element shows that the fault is not related to the Body element. This can be used to distinguish whether the Body element was even processed and is your key to show that the message was received but that an application-specific error was indeed thrown. This element is also the key to the implementation pattern: SOAP Exception Fault Builder. According to the SOAP specification, “other SOAP Fault subelements may be present, provided they are namespace-qualified.”

An example of a SOAP Fault generated from the Microsoft SOAP Toolkit 2.0 SoapServer object is as follows (here it places an <errorInfo> element in the <detail> element to convey detailed error information to the client):

Listing 2.7 Sample Soap Fault Detail Block.

<soap:Fault ...>
 ...
<detail>
   <mserror:errorInfo
         xmlns:mserror="http://schemas.microsoft.com/soap-
           toolkit/faultdetail/error/">
   <mserror:returnCode>-2147024809</mserror:returnCode>
   <mserror:serverErrorInfo>
         <mserror:description>Failure...shutting down</mserror:description>
         <mserror:source>error-source</mserror:source>
         <mserror:helpFile>
               help goes here
         </mserror:helpFile>
         <mserror:helpContext>-1</mserror:helpContext>
   </mserror:serverErrorInfo>
   <mserror:callStack>
         <mserror:callElement>
              <mserror:component>SomeOp</mserror:component>
              <mserror:description>
                    Executing method ReturnError failed
              </mserror:description>
              <mserror:returnCode>-2147352567</mserror:returnCode>
        </mserror:callElement>
  </mserror:callStack>
 </mserror:errorInfo>
</detail>

This contains all of the application-specific error information as generated from the Toolkit's SoapServer object. Your own custom detail element does not necessarily need to look like this but this provides a decent example of what can be done. The more information you provide, the more robust your error handling will become, especially when this information is provided in a SOAP Fault-friendly manner.

As mentioned earlier, throwing normal exceptions using the .NET framework's SOAP exception wrapper may not always provide the details you were expecting. In fact, the main error message provides only the same generic text message: “Server was unable to process request.” To provide a slightly more robust SOAP exception with a custom SOAP Fault, you need to throw your own SoapException class. By doing, so you can provide a much richer exception and still provide a SOAP-compliant fault mechanism so that all clients can better determine the error generated by the Web service. Fortunately, there is a simple way to throw SoapExceptions from your Web service without requiring much effort. In the next section, I will show you exactly how to implement this pattern by adding support to your base exception class. See the beginning of this chapter for details on building a base exception class.

Throwing Custom SOAP Exceptions

In some cases, it is difficult to find areas where Microsoft has not provided a feature desired in its framework. In fact, you have look a little closer before realizing where you could truly add benefit by adding custom behavior. Such is the case for processing exceptions from Web services when using SOAP.

As mentioned in the previous section, when you throw an exception from a Web service that is called from SOAP .NET, this automatically wraps your exception class in the SoapException class. For a remote client, receiving that exception will come in the form of a partially populated SOAP fault. I say partially because .NET does not fill in every detail. I've built a simple Web service to demonstrate this, as shown in Figure 2.4.

02fig04.jpgFigure 2.4. ExceptionThrower.asmx test harnesses a Web service for throwing custom SOAP exceptions.

In Figure 2.4, you'll find a Web service called ExceptionThrower.asmx displayed in a browser. Here you simply specify whether you want to throw a custom SOAP exception using the following implementation pattern or using the framework wrapper. Specifying True in the edit box and selecting Invoke, you should see the output shown in Figure 2.5.

02fig05.jpgFigure 2.5. ExceptionThrower.asmx exception output when selecting custom soap exception.

If you run this again but select False, you should not be able to view the exception text. This is an apparent side effect to not throwing your own SOAP exceptions, at least for Beta 2. This, however, is not the real reason to throw your SOAP exceptions. To the see the real benefit of throwing your own custom SOAP exceptions, run the exceptionthrower.asmx Web service from a typical SOAP 2.0 client (Figure 2.6).

02fig06.jpgFigure 2.6. Called from a SOAP 2.0 client written in VB 6.0 (using wrapper SOAP exceptions). Does not show any SOAP Fault details.

Using the test GUI, I am not selecting Custom Soap before I select Execute. This will call the same Web service with a parameter of False, thus using the .NET wrapper method. Notice that .NET wraps our exception in a SoapException but it provides us with a general error text before the actual error message. More critically, it does not provide us with any details (the <detail/> element is empty). Finally, it does not tell us where the error came from in the form of the faultfactory property. Now when we turn on the Custom Soap option, we should receive a much more detailed SOAP Fault (Figure 2.7).

02fig07.jpgFigure 2.7. Called from a SOAP 2.0 client written in VB 6.0 (using wrapper SOAP exceptions). Shows the SOAP Fault details.

Notice in the previous screen shot that a few things have now been provided. The faultcode property has been set to detail. This is optional but now we have the flexibility to set this faultcode appropriately. For more information on SOAP Fault codes, please refer to the relevant technology backgrounder in this chapter. Also notice that the general description has been replaced with our specific error message. Next, the faultfactory property has been set to the location of the exception. In this case, that location is the actual URI of the Web service. Finally and most important, the details property has been set to the root cause of the error. The information provided in this property can be as detailed as you like. In fact, this would be a great place to display an error stack; as long as the details are properly formatted as SOAP-compliant, the choice is yours. Next I will show you the code behind this implementation pattern.

Adding SOAP Exception Support to BaseException

In this implementation pattern, I will place the custom SOAP exception inside of our BaseException class. This provides two benefits. First it encapsulates the details of the SoapException class from the thrower of the exception, such as a Web service method. Second, it simplifies the mechanism by which all exceptions are thrown. The easier and more standard you can make exception handling, the better. The exceptionthrower.asmx Web service, shown below, shows how to throw or not to throw a custom SOAP exception:

Listing 2.8 Sample Soap Fault Builder for our base exception class.

using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Web;
using System.Web.Services;

namespace ExceptionHandling
{
   . . .

   [WebMethod]
   public void ThrowAnException(bool bUseSoapFault)
   {
         if (bUseSoapFault)
               throw new BaseException(this, 1234,
                           "This is a test 1,2,3,4,5",
                           new Exception(
                           "this is the chained message",
                           null), false, true);
         else
               throw new BaseException(this, 0,
                           "This is a test 1,2,3,4,5",
                           new Exception(
                           "this is the chained message",
                           null), false);
         }
   }
}

The code in Listing 2.8 simply passes true or false as the last parameter of one of our BaseException class constructors. If you pass true, the first BaseException shown is thrown; otherwise, the same signature we used in the constructor defined in the beginning of this chapter is used.

Passing true will call the following constructor.

Listing 2.9 Sample BaseException ctor for allowing Soap Faults.

/// <summary>
/// This ctor for throwing soap exception usually from web service
/// methods
/// This will format error message in more soap fault friendly
/// manner
/// filling in fields not filled in by the default wrapper exception
/// method in .NET
/// </summary>
/// <param name="oSource"></param>
/// <param name="nCode"></param>
/// <param name="sMessage"></param>
/// <param name="oInnerException"></param>
/// <param name="bLog"></param>
/// <param name="bThrowSoap"></param>
public BaseException(object oSource,
                     int nCode,
                     string sMessage,
                     System.Exception oInnerException,
                     bool bLog,
                     bool bThrowSoap) : this(oSource,
                                             nCode,
                                             sMessage, oInnerException, bLog)
{
   string sCause = "no root cause found";
   XmlNode oNode = null;

   if (bThrowSoap)
   {
         if (oInnerException != null)
               sCause = oInnerException.GetBaseException().Message;

         // now build the details node to use for the soap exception
         // -- use the root cause for the main text oNode =
            BuildDetailNode(sCause);

         Trace.WriteLine("Throwing Custom Soap Exception - Message "
                           + sMessage);

         // build actor or source uri to set into actor field –
         // replace .'s with /'s to make a uri from it
         // NOTE: the web service must namespace match the reqeues
         // uri if this is to be accurate
         // we can't use context without passing it so we'll build
         // one
         StringBuilder sActor = new StringBuilder("http://");
         sActor.Append(Dns.GetHostName());
         sActor.Append("/");
         sActor.Append("dotnetpatterns");
         sActor.Append("/");
         sActor.Append(oSource.ToString().Replace(".", "/"));
         sActor.Append(".asmx");

         throw new SoapException(sMessage,
                           SoapException.DetailElementName,
                                 sActor.ToString(), oNode,
                                 oInnerException);
   }
}

There are two main parts to this pattern. First, there is the building of the details property so that we can fully extract SOAP Fault details from the client. The second is the fact that I am “rethrowing” a SoapException back, once the properties are filled. To rethrow a SoapException, we simply call throw new SoapException like any other exception, and the exception should be thrown back to the calling client. Before I do that, however, I need to fill four properties that will become the SOAP Fault elements.

Each of the four properties that make up the SOAP fault will be passed to the SoapException constructor, as shown in Listing 2.9. The first parameter is any message text. Typically, as is the case here, it will be the high-level error message text we've sent when throwing our main exception. The second parameter designates the SOAP Fault code, as defined by the SOAP specification. Although this can be set to any code and your code will still work, it is considered “well formed” if you comply with standards. For this example, I've set this parameter to the detail property because I will be providing a detail explanation as part of the fourth parameter. The third parameter is the actor and is used to set the location of where the error occurred in the form of a URI. Here I dynamically determine the full URI path based on the source object provided and the machine on which this service is running. The oSource.ToString() will give me a fully qualified path string, including the namespace of the assembly under which the source object resides. This will only work if your Web service path matches this fully qualified name and is used only as an example here.

Finally, the fourth parameter provides the details section of the SOAP Fault and is a little more complicated to build so I've supplied a helper method called BuildDetailNode, as shown here:

Listing 2.10 Sample Soap Fault Detail Node Builder.

/// <summary>
/// Build the xml node used for throwing custom soap exceptions, the /// root cause string
graphics/ccc.gif will be use for the main content
   /// </summary>
   /// <param name="sCause"></param>
   /// <returns></returns>
   public System.Xml.XmlNode BuildDetailNode(string sCause)
   {
   XmlDocument oDoc = new XmlDocument();
   XmlNode oNode = oDoc.CreateNode(XmlNodeType.Element,
   SoapException.DetailElementName.Name,
   SoapException.DetailElementName.Namespace);
   
   // Build specific details for the SoapException.
   // Add first child of detail XML element.
   XmlNode oDetailsNode = oDoc.CreateNode(XmlNodeType.Element,
   "rootcause", "http://www.etier.com/patterns.net");
   oDetailsNode.InnerXml = sCause;
   
   oNode.AppendChild(oDetailsNode);
   return oNode;
   }
   

This method uses the System.Xml.XmlDocument and XmlNode classes to build a SOAP Fault-compliant XML node. Once built, this node will be passed back and used as our detail SOAP Fault property. This can be built any way you see fit, as long as the parent detail element node exists along with its namespace; otherwise, it will not be properly displayed in your SOAP client. This SoapException.DetailElementName type is used from within our first CreateNode() call for just this purpose. After this step, you can supply your own text or custom-nested elements. In this example, I add another child node called rootcause and specify the text of the root cause or error, as supplied by the System.Exception class. Another variation of this pattern would be to add the full error stack as child nodes so that the SOAP fault client can see every detail of the error. Either way, the choice is yours, so have fun with it.

COM (Interop) Exception Handling

The common language runtime (CLR) seamlessly provides the ability to access COM objects, as well as allowing COM clients to access .NET code. The CLR's interoperability features also provide sophisticated exception handling when the managed layer is crossed during error scenarios. When a COM client calls a managed piece of code that throws an exception, the CLR intercepts the exception and appropriately translates it into an HRESULT. Similarly, if managed code is accessing an unmanaged COM component that returns an HRESULT, the CLR translates it into an exception. While doing so, the CLR incorporates any additional information provided by the COM IErrorInfo interface into the thrown managed exception class. Any unrecognized HRESULT will result in a generic ComException being thrown with the ErrorCode set to the unresolvable HRESULT.

When using COM components from managed code, this does not present many problems. However, when COM clients currently access your managed code, many of the FCL exceptions will return an HRESULT that may be unrecognizable to the calling COM client. Cases where the calling COM client must handle certain HRESULTs in an application-specific manner can cause problems. Fortunately, the System.Exception object has an HResult property that can be overridden and set at any time during your error handling. To avoid problems with legacy COM clients, replace any HRESULT with that of one that is recognized by those clients to avoid such problems.

Using XML

The output format is completely up to you. A proprietary format was used in the previous examples but XML could have been used. Be forewarned, however, that this output may have multiple targets, each with its own viewers. For example, XML may not be the best display format if you are writing to the Windows 2000 Event Log. A large XML stack does not look particularly great in the Windows Event Viewer.

Another point to keep in mind is when building XML documents, be careful how you format any error strings. If the error strings contain characters interpreted as XML syntax, you may have problems formatting such errors. For example, in the previous section we built a detail XML node that made up one of the properties of our SOAP Fault. The text that was used in the detail node was the root cause of the error. If any of the text strings used for the message inside of the detail XML node happened to contain a “<” or “>” character, a format exception would have been thrown. Having errors occur in your own error handling is not something you want to have happen because it may make communicating them impossible. In fact, you may be a little embarrassed that your own error-handling code has bugs. This is not to say that you should avoid XML in your error handling. Rather, it is quite the contrary. I point this out only so that you can be extra careful when building your base exception class. The more you test your code in this case, the better.

Determining When to Log

Determining when to actually log your output can easily become one of those heated design debates. To keep things simple, however, I suggest logging at the most externally visible tier, or what I've been calling the exception boundary. This coincides with what .NET considers an application and where you specify your configuration files. This is also known as the application level. For GUI development, this is simple: Log just before you display your error. For Web service applications or for those that do not necessarily have a visible tier, I typically log just before returning from an external Web method. This refers only to error logging. Random tracing is another story and is completely dependent on how much output you want to receive and when you want to begin seeing it. For those really nasty bugs, providing a real-time, persistent tracing scheme where you trace as much as possible may be your best option. There is a way to have the best of both worlds—the ability to trace verbose information but only during times where it may be deemed necessary. We will be covering this practice in the next section.

Enabling the Trace Option

By default, C# enables tracing with the help of Visual Studio.NET by adding the /d:TRACE flag to the compiler command line when you build in this environment. Building under Visual Studio.NET automatically will provide this switch for both debug and release builds. Therefore, any tracing can be viewed in either release or debug, which is something I am counting on for the following implementation pattern. Another option for adding tracing is to add #define TRACE to the top of your C# source file. The syntax and mechanism to enable tracing is compiler-specific. If you are working with VB.NET or C++, for example, your settings are slightly different. However, each .NET language should support tracing, so refer to your documentation for details.

Challenge 2.4

How would you send logging output (tracing output) to a remote terminal across the Internet?

A solution appears in the upcoming section on remote tracing (page 82).

The target of the trace output is then determined by what are called trace listeners—the subject of the next technology backgrounder. If you are currently already familiar with trace listeners and dynamic tracing using trace switches, you can skip this section.

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020