Chapter 14. Custom SOAP Headers

In this chapter, you will learn how to:

  • Use SOAP headers in XML Web services.

  • Use SOAP headers in XML Web service clients.

Simple Object Access Protocol (SOAP) was designed to be simple and flexible. As a consequence, the SOAP specification does not include many features that you would expect from a distributed computing protocol, features such as ­security, encryption, transaction support, and message routing. The intention behind keeping the SOAP specification basic is that additional functionality will be defined in separate specifications and layered on top of the basic foundation provided by SOAP. This approach allows SOAP to remain simple, lightweight, and broadly applicable.

SOAP is very flexible because it imposes few restrictions on what data can be transmitted within a SOAP message; in our examples we have used SOAP to pass everything from simple integer values to complex DataSet structures. Sometimes, however, you need to send data in a SOAP message that is not directly related to the function of the XML Web service method being invoked. This data could transcend the individual Web method call; for example, session information, security credentials, or transaction identification details. It would be impractical if you had to include arguments in the signatures of each of your XML Web service methods to accommodate such information.

The SOAP protocol defines SOAP headers that cater to these situations. SOAP headers form part of a SOAP message but are independent of the main body of data contained in the SOAP message. SOAP headers are assigned type names to define their purpose and to uniquely identify them from other SOAP headers. A SOAP message can contain many SOAP headers of different types; depending on the nature of the SOAP header, having multiple headers of the same type might also be valid. Each SOAP header contains the actual data that necessitates the inclusion of the SOAP header within the SOAP message. This data can be any sort of data that can be rendered into XML.

Using Custom SOAP Headers in XML Web Services

To demonstrate the use of SOAP headers, we will develop an XML Web service that implements a custom authentication mechanism. Compared to the IIS authentication mechanisms we discussed in Chapter 13, our authentication mechanism is primitive and not suitable for use in a production environment. However, the example clearly demonstrates both the use of SOAP headers and the basic principles you would use to develop a custom authentication solution—one that does not depend on Windows user accounts and can be implemented on the basis of individual Web methods.

Developing an XML Web Service That Uses Custom SOAP Headers

We’ll base this chapter’s XML Web service on the ValidatorService we developed in the section "The Global.asax file" in Chapter 12. However, in this version we restrict access to the ValidateCard Web method so that only those users who provide the correct credentials can validate a credit card number. We implement only a single hard-coded set of credentials in our example, but with more work we could validate the user credentials against a database of user accounts.

Calls to the ValidateCard Web method must contain a SecurityHeader SOAP header that contains the user name and password. If the user name is "admin" and the password is password, the request is authenticated and the credit card number is validated as expected. In the event of successful authentication, we pass the time at which authentication occurred back to the client as part of the SOAP response, again in a SecurityHeader SOAP header. If the authentication fails, we throw a SoapHeaderException. The SoapHeaderException works the same as the SoapException discussed in Chapter 7, but we use it to indicate problems when processing SOAP headers. The following table summarizes the fields of the SecurityHeader SOAP header.

Name

Description

o_user

A String containing the name of the user calling the ValidateCard Web method.

o_password

A String containing the user’s password.

o_timestamp

A String containing the time at which the user’s request was authenticated. The purpose of this field is to demonstrate the use of SOAP headers in communicating information from an XML Web service back to a client application.

Adding SOAP header support to an XML Web service is a simple process. Most of the run-time processing is handled by ASP.NET automatically. The only steps you need to take are summarized in the following list. Later in the chapter, we will develop a complete example.

  1. Create a class derived from the System.Web.Services.Protocols.SoapHeader class to represent the SOAP header. The public fields and properties of this class define the data that will be transmitted as the content of the SOAP header.

  2. Add a public member of the type defined in step 1 to the XML Web service class. When processing a SOAP header contained in a client request or preparing a SOAP header to send back to a client, you manipulate the header data through the fields of this member.

  3. Apply the SoapHeader attribute (from the System.Web.Services.Protocols namespace) to the XML Web service method or methods in which you want to process the SOAP header. The properties of the SoapHeader attribute (listed in the following table) determine how ASP.NET handles the SOAP header.

    Property

    Description

    MemberName

    The name of the XML Web service member in which to store the object that represents the SOAP header content.

    Direction

    Specifies whether the SOAP header is intended for the XML Web service, the client application, or both. Direction is specified using the members of the System.Web.Services.Protocols.SoapHeaderDirection enumeration. Valid values are:

    • In—The SOAP header is sent to the XML Web service. When a SOAP message is received, ASP.NET creates an instance of the SOAP header class, populates it with the data contained in the SOAP header, and assigns it to the XML Web service member specified by the MemberName property.

    • Out—The SOAP header is sent to the XML Web service client. When the XML Web service method completes, the .NET runtime takes the values from the XML Web service member identified by the MemberName property and packages them into a SOAP header that is returned to the client.

    • InOut—The SOAP header is sent to both the XML Web service and the client. Combines the activities described in both the In and Out values above.

    Required

    Indicates whether the SOAP header must be understood and processed by the recipient.

Procedure 14-1. Copy the XML Web Service Project

  1. Using the Copy Project command in Visual Studio .NET, copy the ValidatorService XML Web service that you created in the section "The Global.asax file" in Chapter 12 We stored this project in the Chapter12/AppEvents Web folder. For the purpose of this example, copy the project to http://localhost/XMLWebServices/Chapter14/SecurityHeader/ValidatorService/.

    Copy the XML Web Service Project
  2. Close the old project, and then open the new one.

  3. Save the new solution files by selecting Save All from the File menu and choosing a suitable location.

Add the SOAP Header Class

  1. Right-click on the ValidatorService project entry in Solution Explorer, and then select Add followed by Add Class from the shortcut menu.

  2. Type SecurityHeader as the name of the new class in the Add New Item dialog box, and then click Open.

    Visual Studio will create a new file named SecurityHeader.cs or SecurityHeader.vb depending on whether you are using C# or Visual Basic .NET.

    Add the SOAP Header Class
  3. Select all the contents of the new SecurityHeader code file and replace it with the code below.

    The SecurityHeader class defined in this file represents the SOAP header that we will use to pass authentication information between the client application and the XML Web service. You can see that the SecurityHeader class contains the three public fields we discussed earlier: o_user, o_password, and o_timestamp. Also notice that we have defined a parameterless constructor; the class must have a parameterless constructor or ASP.NET will not be able to instantiate the class when a request is received.

    Example 14-1. C#

    using System.Web.Services.Protocols;
    
    namespace ValidatorService {
    
        // Class that represents the content of the Soap Header
        // used to secure the XML Web service.
    
        public class SecurityHeader : SoapHeader {
            public string o_user;
            public string o_password;
            public string o_timestamp;
    
            public SecurityHeader() : this("","") {
            }
    
            public SecurityHeader(string x_user, string x_password) {
                o_user = x_user;
                o_password = x_password;
                o_timestamp = "";
            }
        }
    }

    Example 14-2. Visual Basic .NET

    Imports System.Web.Services.Protocols
     
    ‘ Class that represents the content of the Soap Header
    ‘ used to secure the XML Web service.
    Public Class SecurityHeader
        Inherits SoapHeader
    
        Public o_user As String
        Public o_password As String
        Public o_timestamp As String
     
        Public Sub New()
            Me.New("","")
        End Sub
     
        Public Sub New(ByVal x_user As String, _
            ByVal x_password As String)
            o_user = x_user
            o_password = x_password
            o_timestamp = ""
        End Sub
    End Class

Procedure 14-2. Update the ValidatorServiceClass

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

  2. Declare a public member in ValidatorServiceClass of type SecurityHeader. This member will hold the data contained in the SecurityHeader SOAP Header when the ValidateCard Web method is invoked. The highlighted code that follows shows the changes you need to make to ValidatorServiceClass to declare the new member.

    Example 14-3. C#

    [WebService(Namespace="http://mycompany.com/webservices/",
        Name="ValidatorService",
        Description="A service to validate credit card numbers.")]
    public class ValidatorServiceClass: System.Web.Services.WebService
    {
        // declare a public member of type 
        // SecurityHeader to hold the SOAP 
        // header received in the request
        public SecurityHeader o_securityHeader;

    Example 14-4. Visual Basic .NET

    <WebService(Namespace:="http://mycompany.com/webservices/", _
        Name:=" ValidatorService", _
        Description:="A service to validate credit card numbers.")> _
    Public Class ValidatorServiceClass
        Inherits System.Web.Services.WebService
    
        ‘ declare a public member of type 
        ‘ SecurityHeader to hold the SOAP 
        ‘ header received in the request
        Public o_securityHeader As SecurityHeader
  3. In the Validation.asmx code view window, locate the ValidateCard method and replace it with the following code; the changes from the existing ValidateCard method are highlighted in bold.

    We have applied the SoapHeader attribute to the ValidateCard method. The constructor argument identifies the name of the ­member in which to store the received SOAP header. The Direction parameter specifies that the SOAP header is included in both requests and responses. The Required parameter specifies that this header is mandatory.

    When the ValidateCard method is invoked, the first operation we perform is to call the AuthenticateUser method. (We will add this method in the following steps.) The AuthenticateUser method returns true if the credentials supplied in the SecurityHeader are correct; ­otherwise, it returns false. If we get a true result, we continue processing the credit card validation request as normal. If we get a false response, we throw a SoapHeaderException, which is returned to the client.

    Example 14-5. C#

    [WebMethod(EnableSession=true)]
    [SoapHeader("o_securityHeader",
        Direction = SoapHeaderDirection.InOut,
        Required = true)]
    public void ValidateCard(ref ValidationObject p_object) {
        // authenticate the user name and password contained 
        // in the Soap Header, it is automatically stored in 
        // the o_securityHeader member as specified by the 
        // SoapHeader attribute.
        if (AuthenticateUser() == false) {
            // authentication failed, throw a SoapHeaderException
            // indicating access denied
            throw new SoapHeaderException("Access Denied", 
                new XmlQualifiedName("Client.AcessDenied"));
        }
    
        // define a boolean to hold the result
        // of the validation request.
        bool x_result = false;
    
        // define a boolean to indicate if the
        // validation request resulted in an
        // exception being thrown by the 
        // CreditCardValidator.dll library
        bool x_error = false;
    
        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
                    // an 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
                    // an VISAcard number
                    x_result = x_validator.ValidateVisa(
                        p_object.o_card_number);
                    break;
                default:
                    // the method should not reach this point; if it
                    // does, it is because 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) {
            // update the variable to indicate 
            // that an exception has been thrown by
            // CreditCardValidator.dll library
            x_error = true;
    
            // return a soap exception with a custom fault 
            // code based on  the type of the application 
            // exception
            throw GetException(x_ex);
        } finally {
            // get the status object for this client
            ClientStats x_stats = GetClientStatistics();
            // increment the counter for the total
            // number of requests
            x_stats.o_total_requests++;
            // if the result was not true, or if an
            // exception was thrown, then update the
            // failed request counter
            if (x_error || !x_result) {
                x_stats.o_failed_validations++;
            }
    
            // set the result status in the validation object
            p_object.o_valid = x_result;
    
            // update the array list held in the application
            // state to reflect the new request
            ArrayList x_list = 
                (ArrayList)Application["ValidationObjects"];
            //      if (x_list == null) {
            //          x_list = new ArrayList();
            //          Application["ValidationObjects"] = x_list;
            //      }
            x_list.Add(p_object);
        }
    }

    Example 14-6. Visual Basic .NET

    <WebMethod(EnableSession:=True), _
        SoapHeader("o_securityHeader", _ 
        Direction := SoapHeaderDirection.InOut, _
        Required := true)> _
    Public Sub ValidateCard(ByRef p_object As ValidationObject)
        ‘ authenticate the user name and password contained 
        ‘ in the Soap Header, it is automatically stored in 
        ‘ the o_securityHeader member as specified by the
        ‘ SoapHeader attribute.
        If AuthenticateUser() = false Then
            ‘ authentication failed, throw a SoapHeaderException
            ‘ indicating access denied
            Throw new SoapHeaderException("Access Denied", _
                new XmlQualifiedName("Client.AcessDenied"))
        End If
    
        ‘ define a boolean to hold the result
        ‘ of the validation request.
        Dim x_result As Boolean
    
        ‘ define a boolean to indicate if the
        ‘ validation request resulted in an
        ‘ exception being thrown by the 
        ‘ CreditCardValidator.dll library
        Dim x_error As Boolean = False
    
        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
                    ‘ an MasterCard card number
                    x_result = x_validator.ValidateMasterCard( _
                        p_object.o_card_number)
                Case CARD_TYPE.VISA
                    ‘ the client has requested that we validate
                    ‘ an VISAcard number
                    x_result = x_validator.ValidateVisa( _
                        p_object.o_card_number)
                Case Else
                    ‘ the method should not reach this point; if it
                    ‘ does, it is because 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
            ‘ update the variable to indicate 
            ‘ that an exception has been thrown by
            ‘ CreditCardValidator.dll library
            x_error = True
    
            ‘ return a soap exception with a custom fault 
            ‘ code based on  the type of the application 
            ‘ exception
            Throw GetException(x_ex)
        Finally
            ‘ get the status object for this client
            Dim x_stats As ClientStats = GetClientStatistics()
            ‘ increment the counter for the total
            ‘ number of requests
            x_stats.o_total_requests = x_stats.o_total_requests + 1
    
            ‘ if the result was not true, or if an
            ‘ exception was thrown, then update the
            ‘ failed request counter
            If x_error Or Not x_result Then
                x_stats.o_failed_validations = _
                    x_stats.o_failed_validations + 1
            End If
    
            ‘ set the result status in the validation object
            p_object.o_valid = x_result
    
            Dim x_list As ArrayList = _
                CType(Application("ValidationObjects"), ArrayList)
            ‘ check to ensure that we have a list to work with - if the
            ‘ value returned from the Application object is null, this 
            ‘ is the first request made to the XML Web service method,
            ‘ and we should create the list for use by future calls
            ‘If x_list Is Nothing Then
            ‘    x_list = New ArrayList()
            ‘    Application("ValidationObjects") = x_list
            ‘End If
            ‘ update the array list held in the application
            ‘ state to reflect the new request
            x_list.Add(p_object)
    
        End Try
    End Sub
  4. Add the new AuthenticateUser method listed below as a member of the ValidatorServiceClass class.

    When the AuthenticateUser method is called, we check the o_user and o_password fields of the o_securityHeader member to see whether they match our hard-coded values of admin and password. If the o_user and o_password fields are correct, we generate a String containing the current date and time and assign it to the o_security­Header.o_timestamp member. This timestamp is passed back to the client application in a SecurityHeader SOAP header as part of the ValidateCard Web method response. If the user name and password are incorrect, we return false.

    Example 14-7. C#

    private bool AuthenticateUser() {
        // perform a case insensitive comparison of the user 
        // name contained in the SOAP header with our hard-coded 
        // account name.
        if (o_securityHeader.o_user.ToLower() == "admin") {
            // perform a case insensitive comparison of the 
            // password contained in the SOAP header with our 
            // hard-coded password.
            if (o_securityHeader.o_password.ToLower() == "password") {
                // user and password are valid, set the timestamp 
                // in the SOAP header to the current time. this will
                // be passed back to the client.
                o_securityHeader.o_timestamp = DateTime.Now.ToString();
    
                // return true to indicate successful authentication
                return true;
            }
        }
        // return false to indicate failed authentication
        return false;
    }

    Example 14-8. Visual Basic .NET

    Private Function AuthenticateUser() As Boolean
        ‘ perform a case insensitive comparison of the user 
        ‘ name contained in the SOAP header with our hard-coded 
        ‘ account name.
        If o_securityHeader.o_user.ToLower() = "admin" Then
            ‘ perform a case insensitive comparison of the 
            ‘ password contained in the SOAP header with our 
            ‘ hard-coded password.
            If o_securityHeader.o_password.ToLower() = "password" Then
                ‘ user and password are valid, set the timestamp 
                ‘ in the SOAP header to the current time. this will
                ‘ be passed back to the client.
                o_securityHeader.o_timestamp = DateTime.Now.ToString()
     
                ‘ return true to indicate successful authentication
                Return True
            End If
        End If
        ‘ return false to indicate failed authentication
        Return False
    End Function
  5. Build the ValidatorService project by selecting Build Solution from the Build menu or pressing Ctrl+Shift+B.

Developing an XML Web Service Client That Uses Custom SOAP Headers

In the following example, we develop a client application to use with the ValidatorService XML Web service that we developed in the previous section. The application is based on the client template introduced in Chapter 7. The key difference in this application is the controls we’ll add for user name and password. When the user clicks the Validate button, the contents of the User Name and Password controls are passed to the XML Web service method in a Security­Header SOAP header where they are used to authenticate the user.

Using SOAP headers in XML Web service clients is simple thanks to the way that Visual Studio .NET generates the client proxy. When processing the service description (WSDL) file for the ValidatorService XML Web service, Visual Studio .NET identifies that a SecurityHeader SOAP header is required by the ValidateCard Web method. In addition to generating the proxy class, Visual Studio .NET generates a class that represents the SecurityHeader SOAP header; the name given to this class is the same as the SOAP header (in our example Security­Header). The SecurityHeader class contains public fields for each of the data elements of the SOAP header: o_user, o_password, and o_timestamp. The proxy class contains a member of type SecurityHeader named SecurityHeaderValue through which we manipulate the contents of the SOAP header.

The following list summarizes the steps necessary to pass a SecurityHeader SOAP header to the XML Web service.

  1. Instantiate a SecurityHeader object.

  2. Set the fields of the SecurityHeader object to the values you want to send to the XML Web service.

  3. Assign the SecurityHeader object to the SecurityHeaderValue member of the proxy class.

When you call the ValidateCard Web method, the proxy generates a SecurityHeader SOAP header from the contents of the SecurityHeader object and sends it to the XML Web service as part of the method invocation. When the Web method returns, if the XML Web service has sent a SecurityHeader SOAP header, the client can access the SOAP header data through the fields of the SecurityHeader object stored in the proxy’s SecurityHeaderValue member.

Procedure 14-3. Create the WindowsFormsClient project

  1. Copy the client template project into a suitable location as described in the sidebar "XML Web Service Client Projects" in Chapter 7. We will use the location C:XMLWebServicesSBSProjectsChap­ter14SecurityHeader.

  2. Open the copy of the client template in Visual Studio .NET.

  3. Create a new Web reference that points to the ValidatorService that we created earlier in this chapter. The Web reference URL is http://localhost/XMLWebServices/Chapter14/SecurityHeader/ValidatorService/Validation.asmx .

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

Procedure 14-4. Edit the Windows Form

  1. Change the Size property of Form1 to 288, 300.

  2. Change the Location property of the ValidateButton button to 109, 240.

  3. Add the controls listed in the following table to Form1 using the Visual Studio .NET toolbox, and then configure their properties as specified.

    Edit the Windows Form

    Control

    Property

    Value

    Label

    Text

    User Name:

     

    TextAlign

    MiddleRight

     

    Location

    8, 120

    Label

    Text

    Password:

     

    TextAlign

    MiddleRight

     

    Location

    8, 160

    TextBox

    Text

    Delete the contents of the Text property value.

     

    Name

    UserName

     

    Location

    144, 120

     

    Size

    128, 20

    TextBox

    Text

    Delete the contents of the Text property value.

     

    Name

    Password

     

    Location

    144, 160

     

    Size

    128, 20

    Label

    Text

    Authenticated at:

     

    TextAlign

    MiddleRight

     

    Location

    8, 200

    Label

    Text

    Delete the contents of the Text property value.

     

    TextAlign

    MiddleLeft

     

    Name

    AuthenticationTime

     

    Location

    144, 200

     

    Size

    128, 23

Procedure 14-5. Implement the WindowsFormsClient Functionality

  1. In the Form1 design view window, double-click on the Validate button to bring up the code view of Form1 and show the ValidateButton_Click method.

  2. Modify the ValidateButton_Click method with the changes (highlighted in bold) in the following code.

    The most important change is the creation of a SecurityHeader instance that we assign to the SecurityHeaderValue member of the proxy class. We set the o_user and o_password fields of the SecurityHeader object prior to making a call to the XML Web service ValidateCard method. The proxy class automatically takes the contents of the SecurityHeader instance and packages them into a SOAP header that is sent to the XML Web service.

    We catch SoapHeaderExceptions that are thrown by the XML Web service and assume they mean that our authentication has failed. Although this is acceptable for our example, it is not a good assumption to make in a production system. You should use the fault code handling techniques described in Chapter 7 to derive more meaning from the contents of a SoapHeaderException.

    Once the call returns, the SecurityHeaderValue member of the proxy class will contain the data returned in the SecurityHeader SOAP header from the XML Web service. We extract the authentication time and display it in the user interface.

    Example 14-9. C#

    private void ValidateButton_Click(object sender, 
        System.EventArgs e) {
        // create a new instance of the XML Web service
        // proxy class
        ValidatorService x_proxy = new ValidatorService();
    
        // 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 {
            
    // create a new instance of the SecurityHeader
            // class and assign it to the SecurityHeaderValue
            // of the proxy object
            x_proxy.SecurityHeaderValue = new SecurityHeader();
    
            // set the values of the SecurityHeader based on the 
            // current UserName and Password
            x_proxy.SecurityHeaderValue.o_user = UserName.Text;
            x_proxy.SecurityHeaderValue.o_password = Password.Text;
    
            // clear the authentication time from the previous
            // request
            AuthenticationTime.Text = "";
    
            // request validation from the XML Web service
            // via the proxy class
            x_proxy.ValidateCard(ref x_object);
    
            // display the authentication time returned from
            // the XML Web service in the SOAP header
            AuthenticationTime.Text = 
                x_proxy.SecurityHeaderValue.o_timestamp;
    
            // 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 (SoapHeaderException x_ex1) {
            // for our purpose, assume access denied
            Result.Text = "Access Denied";
        
    } catch (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 14-10. Visual Basic .NET

    Private  Sub ValidateButton_Click(ByVal sender As Object, _
        ByVal e As System.EventArgs) Handles ValidateButton.Click
        ’ create a new instance of the XML Web service
        ’ proxy class
        Dim x_proxy As ValidatorService = new ValidatorService()
    
        ‘ 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
            
    ’ create a new instance of the SecurityHeader
            ’ class and assign it to the SecurityHeaderValue
            ’ of the proxy object
            x_proxy.SecurityHeaderValue = new SecurityHeader()
    
            ’ set the values of the SecurityHeader based on the 
            ’ current UserName and Password
            x_proxy.SecurityHeaderValue.o_user = UserName.Text
            x_proxy.SecurityHeaderValue.o_password = Password.Text
    
            ‘ clear the authentication time from the previous
            ‘ request
            AuthenticationTime.Text = ""
    
            ’ request validation from the XML Web service
            ’ via the proxy class
            x_proxy.ValidateCard(x_object)
    
            ‘ display the authentication time returned from
            ‘ the XML Web service in the SOAP header
            AuthenticationTime.Text = _
                x_proxy.SecurityHeaderValue.o_timestamp
    
            ‘ 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_ex1 As SoapHeaderException
            ‘ for our purpose, assume access denied
            Result.Text = "Access Denied"
        
    Catch x_ex As 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. Build the WindowsFormsClient application by selecting Build Solution from the Build menu or pressing Ctrl+Shift+B.

Procedure 14-6. Test WindowsFormsClient

  1. Run the WindowsFormsClient application by pressing Ctrl+F5 in Visual Studio .NET.

  2. Select VISA as the card type, and then enter 4921 8352 2155 2042 as the card number.

  3. Enter the user name allen and the password mypassword.

    Test WindowsFormsClient
  4. Click Validate.

    Because we entered an invalid user name and password, the credit card validation request fails. The client displays the result "Access Denied".

    Test WindowsFormsClient
  5. Replace the user name with admin and the password with password; these are the account details that we hard-coded into our XML Web service.

  6. Click Validate.

The new user name and password match the account details held by the ValidatorService, so the validation request is processed and the credit card number is reported to be valid. The client displays the result "Number Valid," and the AuthenticatedTime label is set to the value of o_timestamp returned in the SecurityHeader SOAP header from the XML Web service. The exact format of the timestamp will vary depending on the configured locale of your machine.

Test WindowsFormsClient

Chapter 14 Quick Reference

To

Do This

Use SOAP headers in an XML Web service

Create a class derived from the System.Web.Services.Protocols.SoapHeader class to represent the SOAP header; the public fields and properties of this class define the data that will be transmitted as the content of the SOAP header. Add a public member of this type to the XML Web service class. Apply the SoapHeader attribute to the XML Web service method or methods in which you want to process the SOAP header.

Control the direction in which a SOAP header is sent.

Use the Direction property of the SoapHeader attribute on the XML Web service method.

Consume an XML Web service that uses custom SOAP headers

Visual Studio .NET generates the necessary facilities when you create a proxy class. A class is created for each SOAP header type used by the XML Web service, and the proxy declares members for each header used. Create an instance of the SOAP header class, set its fields to the appropriate values, and assign it to the appropriate proxy class member. When you call the XML Web service methods, the proxy class packages and unpackages the data for the SOAP header.

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

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