Chapter 7. Exception Handling

In this chapter, you will learn how to:

  • Throw exceptions in XML Web services.

  • Handle exceptions thrown by XML Web services.

  • Use custom fault codes to better report errors in XML Web services.

Exceptions are a powerful programming technique, supported by both C# and Visual Basic .NET, that provides a structured approach to handling system and application error conditions. You can use exceptions within the scope of your XML Web services as you do in any other application. Only when an XML Web service throws an exception that must be propagated back to the client is special consideration required.

How ASP.NET Handles Exceptions

When an XML Web service throws an exception that it does not handle ­internally, the exception passes to ASP.NET. The action taken by ASP.NET depends on which protocol the client is using to communicate with the XML Web service.

  • Communicating Exceptions to HTTP Clients

    If the client is using HTTP-GET or HTTP-POST, ASP.NET returns an HTTP Internal Server Error (Error code 500) to the client. The body of the error response contains a string representation of the exception’s stack trace. The client must determine the appropriate action to take in response to the error, but the only information it can base the decision on is the stack trace string. Parsing the string information is relatively expensive computationally and far from straightforward when compared to the normal process of exception handling.

    Things are slightly better if the client is using a .NET HTTP proxy class. The proxy receives the HTTP error and raises a System.Net.WebException in response, avoiding the need to manipulate HTTP messages directly and enabling the use of normal exception handling techniques. The stack trace string is contained in the WebException.Message property, but the string still requires processing to determine the cause of the error.

  • Communicating Exceptions to SOAP Clients

    If the client is using SOAP, ASP.NET catches the unhandled exception, encodes it as a SOAP Fault, and passes it back to the client. The SOAP specification defines a SOAP Fault as a mechanism for encapsulating and transmitting error information using SOAP messages. SOAP Faults consist of fields that contain information about the error that occurred. Although SOAP Faults do not provide a direct mapping for encoding the complete state of a .NET exception, they do provide more flexibility than the simple stack trace string available in the case of HTTP.

    Two key SOAP Fault fields are faultcode and faultstring. The faultcode field identifies the fault that has occurred; by default, ASP.NET always sets this field to a value indicating a server error, regardless of the fact that the error could have been caused by bad data from the client. The faultstring field contains human readable information about the error. ASP.NET places a string representation of the caught exception’s stack trace in faultstring.

    When a client using a SOAP proxy receives a SOAP Fault, it behaves in a manner similar to the HTTP proxy that we described earlier; however, the SOAP proxy raises a System.Web.Services.Protocols.SoapException. Additionally, the proxy sets the SoapException.Code property to the value of the SOAP Fault’s faultcode field and the SoapException.Message property to the value of faultstring. The following table shows SoapException to SOAP Fault mappings.

    SoapException property

    SOAP Fault element

    Default contents

    Code

    faultcode

    Server error

    Message

    faultstring

    String representation of caught exception’s stack trace

    Unfortunately, this mechanism so far is no better than the HTTP scenario discussed earlier. No matter what exception the XML Web service throws, the client proxy always throws a SoapException. Because the SoapException.Code property always indicates a generic server error, the client must still parse the stack trace string contained in the SoapException.Message property to determine the details of the exception thrown.

Using Custom Fault Codes

The way to overcome these issues is surprisingly simple. Instead of allowing ASP.NET to handle uncaught client-bound exceptions automatically, your XML Web service must capture all application exceptions and explicitly throw a SoapException. When ASP.NET catches a SoapException, it correctly fills in the SOAP Fault faultcode and faultstring elements using the Code and Message properties of the SoapException. This enables you to return custom fault codes that provide meaningful error information to the client application. The client can then catch the SoapException and perform the appropriate action by using the fault code provided by the SoapException.Code property.

Important

One disadvantage of using custom fault codes is that developers of client applications must know, and explicitly handle, the codes. Because this information is not contained in the WSDL document for the XML Web service, it must be communicated in your system’s documentation.

Throwing Exceptions in XML Web Services

Armed with an understanding of exceptions in XML Web services, SOAP Faults, and custom fault codes, we will add a more robust and informative error-handling solution to our credit card validation service in the procedures that follow. We will develop a new XML Web service based on the ValidatorService XML Web service that we created in Chapter 6.

We will modify the exception handling code that wraps our calls to methods contained in CreditCardValidator.dll. Instead of catching all exceptions and setting the validation result to false, we catch all ApplicationException exceptions that are thrown and generate a SoapException with a custom fault code. To set the custom fault code, we use a constructor of the SoapException class that lets us set the Code and Message properties. The only thing to be aware of is that the SoapException.Code property is not a String but an instance of System.Xml.XmlQualifiedName. You must instantiate an XmlQualifiedName based on the String you want to return.

The following table summarizes the exceptions the methods of the Validator class can throw and maps them to the custom fault codes we will assign. Full details of the Validator class and the CreditCardValidation.dll library are available in Appendix A.

Exception

Fault Code and Description

CCIllegalCharacterException

Fault code: Client.IllegalCharacter Thrown if the credit card number passed to the method contains illegal characters. Only decimal digits and white space are valid in credit card ­numbers.

CCInvalidLengthException

Fault code: Client.InvalidLength Thrown if the credit card number passed to the method has the wrong number of digits for the ­type of card.

CCInvalidPrefixException

Fault code: Client.InvalidPrefix Thrown if the credit card number passed to the method does not have the expected prefix for the card type. For example, all valid VISA cards must start with the digit 4.

Procedure 7-1. Copy the XML Web Service Project

  1. Use the Copy Project function in Visual Studio .NET to copy the ValidatorService XML Web service that you created in the section "Reference Arguments" in Chapter 6.

    For this example, copy the ValidatorService project to http://localhost/XMLWebServices/Chapter07/CustomFaultCodes/Validator­Service/.

    Copy the XML Web Service Project
  2. Ensure that you close the old project and open the new one before proceeding. Save the new solution files by selecting Save All from the File menu and choosing a suitable location.

Procedure 7-2. Add the Exception Handling Code

  1. In Solution Explorer, right-click on the Validation.asmx file and select View Code from the shortcut menu.

  2. In the Validation.asmx code view window, locate the ValidateCard method and replace it with this code.

    The new ValidateCard method replaces the exception handling code that was associated with the credit card validation calls to CreditCardValidation.dll. Instead of catching all exceptions and simply setting p_object.o_valid to false for return to the client, we now catch the exception and throw a SoapException by calling the GetException method. We discuss the addition and purpose of the GetException method in step 3.

    Example 7-1. C#

    [WebMethod]
    public void ValidateCard(ref ValidationObject p_object) {
        // define a boolean to hold the result
        // of the validation request
        bool x_result;
    
        try {
            // create the validator instance that we will
            // use to check the card number
            Validator x_validator = new Validator();
    
            // use the type of card supplied in the 
            // validation object to determine which
            // library method we should call in order
            // to perform the validation
            switch (p_object.o_card_type) {
                case CARD_TYPE.AMEX:
                    // the client has requested that we validate
                    // an AMEX card number
                    x_result = x_validator.ValidateAMEX(
                        p_object.o_card_number);
                    break;
                case CARD_TYPE.MASTERCARD:
                    // the client has requested that we validate
                    // a MasterCard card number
                    x_result = x_validator.ValidateMasterCard(
                        p_object.o_card_number);
                    break;
                case CARD_TYPE.VISA:
                    // the client has requested that we validate
                    // a VISA card number
                    x_result = x_validator.ValidateVisa(
                           p_object.o_card_number);
                    break;
                default:
                    // the method should not reach this point; if it
                    // does, then the client has specified
                    // a value which is not contained in the CARD_TYPE
                    // enumeration. Should this happen, the client has 
                    // requested that we validate a card type that we 
                    // do not support.
                    x_result = false;
                    break;
            }
        } catch (ApplicationException x_ex) {
            // return a soap exception with a custom fault 
            // code based on the type of the application 
            // exception
            throw GetException(x_ex);
        }
    
        // set the result status in the validation object
        p_object.o_valid = x_result;
    }

    Example 7-2. Visual Basic .NET

    <WebMethod()> _
    Public Sub ValidateCard(ByRef p_object As ValidationObject)
        ’ define a boolean to hold the result
        ’ of the validation request
        Dim x_result As Boolean
    
        Try
            ’ create the validator instance that we will
            ’ use to check the card number
            Dim x_validator As Validator = New Validator()
    
            ’ use the type of card supplied in the 
            ’ validation object to determine which
            ’ library method we should call in order
            ’ to perform the validation
            Select Case p_object.o_card_type
                Case CARD_TYPE.AMEX
                    ’ the client has requested that we validate
                    ’ an AMEX card number
                    x_result = x_validator.ValidateAMEX(_
                        p_object.o_card_number)
                Case CARD_TYPE.MASTERCARD
                    ’ the client has requested that we validate
                    ’ a MasterCard card number
                    x_result = x_validator.ValidateMasterCard(_
                        p_object.o_card_number)
                Case CARD_TYPE.VISA
                    ’ the client has requested that we validate
                    ’ a VISA card number
                    x_result = x_validator.ValidateVisa(_
                        p_object.o_card_number)
                Case Else
                    ’ the method should not reach this point; if it
                    ’ does, then the client has specified
                    ’ a value which is not contained in the CARD_TYPE
                    ’ enumeration. Should this happen, the client has 
                    ’ requested that we validate a card type that we 
                    ’ do not support.
                    x_result = False
            End Select
        Catch x_ex As ApplicationException
            ’ return a soap exception with a custom fault 
            ’ code based on the type of the application 
            ’ exception
                Throw GetException(x_ex)
        End Try
    
        ’ set the result status in the validation object
        p_object.o_valid = x_result
    End Sub 
  3. Add the new GetException method as a member of the ValidatorServiceClass class.

    The GetException method takes an ApplicationException as an argument. The method instantiates a new SoapException object and sets the Code and Message properties of the exception depending on the type of exception it was passed.

    Example 7-3. C#

    private SoapException GetException(ApplicationException x_ex) {
        XmlQualifiedName x_name;
        if (x_ex is CCIllegalCharacterException) {
            x_name = new XmlQualifiedName("Client.IllegalCharacter");
        } else if (x_ex is CCInvalidLengthException) {
            x_name = new XmlQualifiedName("Client.InvalidLength");
        } else if (x_ex is CCInvalidPrefixException) {
            x_name = new XmlQualifiedName("Client.InvalidPrefix");
        } else {
            x_name = SoapException.ServerFaultCode;
        }
        return new SoapException(x_ex.Message, x_name);
    }

    Example 7-4. Visual Basic .NET

    Private Function GetException(ByVal x_ex As ApplicationException) _
        As SoapException
        Dim x_name As XmlQualifiedName
        If TypeOf x_ex Is CCIllegalCharacterException Then
            x_name = New XmlQualifiedName("Client.IllegalCharacter")
        ElseIf TypeOf x_ex Is CCInvalidLengthException Then
            x_name = New XmlQualifiedName("Client.InvalidLength")
        ElseIf TypeOf x_ex Is CCInvalidPrefixException Then
            x_name = New XmlQualifiedName("Client.InvalidPrefix")
        Else
            x_name = SoapException.ServerFaultCode
        End If
        Return New SoapException(x_ex.Message, x_name)
    End Function
  4. Insert the following statements at the start of the Validation.asmx code view to import the SoapException and XmlQualifiedName classes.

    Example 7-5. C#

    // the following statements allow us to create
    // SOAP exceptions with custom fault codes
    using System.Web.Services.Protocols;
    using System.Xml;

    Example 7-6. Visual Basic .NET

    ‘ the following statements allow us to create
    ‘ SOAP exceptions with custom fault codes
    Imports System.Web.Services.Protocols
    Imports System.Xml
  5. Select Build Solution from the Build menu (or press Ctrl+Shift+B) to build the ValidatorService project.

Handling Exceptions in XML Web Service Clients

We now have an XML Web service that uses exceptions and custom fault codes to notify clients of error conditions that occur during the validation of a credit card number. No longer can our client application simply process Boolean responses; it must also handle the exceptions that might occur in response to the validation requests we submit. In the procedures that follow, we’ll develop a new client application that handles exceptions and custom fault codes returned by the XML Web service it consumes.

Procedure 7-3. Create the WindowsFormsClient project

  1. Create a new Visual Studio .NET project using either the C# or Visual Basic .NET Windows Application project template. In the New Project dialog box, name the project WindowsFormsClient and specify C:XMLWebServicesSBSProjectsChapter07CustomFaultCodes for the location.

    Create the WindowsFormsClient project
  2. Create a Web reference that points to the ValidatorService that we created earlier in this chapter, using the URL http://localhost/XMLWebServices/Chapter07/CustomFaultCodes/ValidatorService/Validation.asmx.

  3. In Solution Explorer, right-click the Web reference and select Rename from the shortcut menu. Rename the Web Reference Validator.

Procedure 7-4. Create the Windows Form

  1. In Solution Explorer, double-click the source file for Form1 (either Form1.cs or Form1.vb depending on which language you are using) to open the design view for Form1.

  2. Choose Properties Window from the View menu (or press F4) to open the Properties window for Form1. Configure the properties as specified in the following table.

    Property

    Value

    Text

    Validator Client

    Size

    288, 208

  3. Use the Visual Studio .NET toolbox to add four labels, a button, a combo box, and a text box to Form1. The form’s user interface is the same as in examples in previous chapters.

    Create the Windows Form
  4. Configure the controls’ properties as shown in the following table.

    Control

    Property

    Value

    Label

    Text

    Card Type:

     

    TextAlign

    MiddleRight

     

    Location

    8, 8

    Label

    Text

    Card Number:

     

    TextAlign

    MiddleRight

     

    Location

    8, 40

    Label

    Text

    Result:

     

    TextAlign

    MiddleRight

     

    Location

    8, 80

    ComboBox

    Text

    VISA

     

    Items

    AMEX MasterCard VISA

     

    Name

    CardType

     

    Location

    144, 8

     

    Size

    128, 21

    TextBox

    Text

    Delete the contents of the Text property value.

     

    Name

    CardNumber

     

    Location

    144, 40

     

    Size

    128, 20

    Label

    Text

    Delete the contents of the Text property value.

     

    TextAlign

    MiddleLeft

     

    Name

    Result

     

    Location

    144, 80

     

    Size

    128, 23

    Button

    Text

    Validate

     

    Name

    ValidateButton

     

    Location

    109, 128

Procedure 7-6. Implement the WindowsFormsClient functionality

  1. In the Form1 design view window, double-click on the Validate ­button.

    This will display Form1 in code view and show the ValidateButton_Click method.

  2. Replace the empty ValidateButton_Click method with the following code.

    This code is slightly different from that which we have used in previous examples. (The differences are highlighted in bold.) The changes cause the client to catch any SoapException exceptions thrown by the ValidatorService.ValidateCard XML Web service method. On catching an exception, the client displays the appropriate message based on the known values of the SoapException.Code property.

    Example 7-7. C#

    private void ValidateButton_Click(object sender, 
        System.EventArgs e) {
        // create the ValidationObject that we will send to 
        // the XML Web service
        ValidationObject x_object = new ValidationObject();
    
        // set the card type for the validation object based
        // on the user selection in the CardType ComboBox
        switch (CardType.Text) {
            case "AMEX":
                x_object.o_card_type = CARD_TYPE.AMEX;
                break;
            case "MasterCard":
                x_object.o_card_type = CARD_TYPE.MASTERCARD;
                break;                                
            case "VISA":
                x_object.o_card_type = CARD_TYPE.VISA;
                break;
        }
    
        // set the card number value in the ValidationObject
        x_object.o_card_number = CardNumber.Text;
    
        try {
            // request validation from the XML Web service
            // via the proxy class
            new ValidatorService().ValidateCard(ref x_object);
    
            // set the text of the result label based
            // on the response from the XML Web service
            if (x_object.o_valid) {
                Result.Text = "Number Valid";
            } else {
                Result.Text = "Number Invalid";
            }
        } catch (System.Web.Services.Protocols.SoapException x_ex) {
            // display the content of the exception
            switch (x_ex.Code.ToString()) {
                case "Client.IllegalCharacter":
                    Result.Text = "Illegal Character";
                    break;
                case "Client.InvalidLength":
                    Result.Text = "Invalid Length";
                    break;
                case "Client.InvalidPrefix":
                    Result.Text = "Invalid Prefix";
                    break;
                default:
                    Result.Text = "Unexpected Error";
                    break;
            }
        }
    }

    Example 7-8. Visual Basic .NET

    Private  Sub ValidateButton_Click(ByVal sender As Object, ByVal e _
        As System.EventArgs) Handles ValidateButton.Click
        ’ create the ValidationObject that we will send to 
        ’ the XML Web service
        Dim x_object As ValidationObject = New ValidationObject() 
     
        ’ set the card type for the validation object based
        ’ on the user selection in the CardType ComboBox
        Select Case CardType.Text
            Case "AMEX"
                x_Object.o_card_type = CARD_TYPE.AMEX
            Case "MasterCard"
                x_Object.o_card_type = CARD_TYPE.MASTERCARD
            Case "VISA"
                x_Object.o_card_type = CARD_TYPE.VISA
        End Select
     
        ’ set the card number value in the ValidationObject
        x_Object.o_card_number = CardNumber.Text
     
        Try
            ’ request validation from the XML Web service
            ’ via the proxy class
            Dim x_service As ValidatorService = New ValidatorService()
            x_service.ValidateCard(x_object)
    
            ’ set the text of the result label based
            ’ on the response from the XML Web service
            If x_Object.o_valid Then
                Result.Text = "Number Valid"
            Else 
                Result.Text = "Number Invalid"
            End If
         Catch x_ex As System.Web.Services.Protocols.SoapException
            Select Case x_ex.Code.ToString()
                Case "Client.IllegalCharacter"
                    Result.Text = "Illegal Character"
                Case "Client.InvalidLength"
                    Result.Text = "Invalid Length"
                Case "Client.InvalidPrefix"
                    Result.Text = "Invalid Prefix"
                Case Else
                    Result.Text = "Unexpected Error"
            End Select
        End Try
    End Sub
  3. Scroll to the top of the Form1 code view window and add the statements to import the proxy class namespace. Also add a statement to import the System.Web.Services.Protocols namespace. This statement gives the code access to the SoapException class.

    Example 7-9. C#

    // import the proxy class
    using WindowsFormsClient.Validator;
    
    // import for SoapException class
    using System.Web.Services.Protocols;

    Example 7-10. Visual Basic .NET

    ‘ import the proxy class
    Imports WindowsFormsClient.Validator
     
    ‘ import for SoapException class
    Imports System.Web.Services.Protocols
  4. Choose Build Solution from the Build menu (or press Ctrl+Shift+B) to build WindowsFormsClient.

Testing the WindowsFormsClient Application

Knowing the exceptions thrown by the Validator class in the CreditCardValidator.dll library, we can do three things to force it to throw an exception:

  • Pass a credit card with nondecimal characters in it

  • Pass a credit card number with an invalid length for the type of card specified

  • Pass a credit card number that has an invalid prefix for the type of card specified

Testing these conditions is simply a matter of putting incorrect data into the client application and clicking the Validate button. In the following procedure, we will force a CCIllegalCharacterException exception at the XML Web service by passing nondecimal characters as the credit card number.

Procedure 7-7. Test Error Conditions

  1. While in Visual Studio .NET, press Ctrl+F5 to run the WindowsFormsClient application.

  2. Select VISA as the card type.

  3. Enter the text baddata as the card number; this will cause an exception because it contains nondecimal characters.

  4. Click Validate. The result will be "Illegal Character," as shown in the following screen shot.

    Test Error Conditions

Chapter 7 Quick Reference

To

Do This

Throw an exception in an XML Web service

Throw the exception as you would normally. ASP.NET will catch it and return an error message to the client if it is not handled by the XML Web service internally.

Handle an exception thrown by an XML Web service when developing an HTTP proxy client

Use standard exception handling syntax and catch exceptions of type System.Net.WebException. Parse the exception stack trace in the WebException.Message property to determine the appropriate action to take.

Handle an exception thrown by an XML Web service when developing a SOAP proxy client

Use standard exception handling syntax and catch exceptions of type System.Web.Services.Protocols.SoapException. If the XML Web service uses custom fault codes, inspect the SoapException.Code property to determine the appropriate action to take. Otherwise, parse the exception stack trace in the SoapException.Message property to determine the appropriate action.

Throw a specific exception and pass it back to the ­client.

This cannot be done. The best you can do is throw a SoapException. The values you set for the Code and Message properties of the SoapException are passed to the client and made available through the SoapException raised by the client proxy.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.7.240