Using SOAP Extensions for Debugging

Earlier in this chapter, we briefly discussed some special considerations for debugging XML Web services. The debugging tools supplied by Visual Studio .NET are useful for making sure that the code inside your Web methods is working correctly. Sometimes it is also necessary to view the SOAP messages that are created and sent back and forth between the Web service and the client application.

In the standard course of operations, SOAP messages are not directly visible, because the components in the .NET Framework that make it easy to create Web services generate the messages automatically. In this section, you are going to learn how to use SOAP extensions. SOAP extensions are classes that you create with your own application-specific processing. Your custom code will run each time a SOAP message is received or sent by a Web service.

Note 

In Exercise 8.3, you will see an example of how to capture the complete SOAP message and store it in a text file. You could use SOAP extensions for other types of logging and debugging purposes as well.

You can create custom SOAP extensions by creating your own Extension classes that inherit from System.Web.Services.Protocols.SoapExtension. You must also create a class that inherits from SoapExtensionAttribute. The SoapExtension class contains the code that will run when a SOAP message is processed. The SoapExtensionAttribute class provides a means to mark a Web method, so that your SOAP extension will be called when the method is invoked. You will then compile these classes into a DLL that will be referenced by your XML Web service.

When you create a class that inherits from SoapExtension, you must override the methods of the base class with your own custom methods. When you create a class that inherits from SoapExtensionAttribute, you must override the property procedures defined by the base class.

Here are the methods of the SoapExtension class that will be implemented:

GetInitializer This method runs the first time an XML Web service or a particular method is called. Values that are initialized in this procedure are cached and can be used for all future method calls on the service.

Initialize This method is called for every method call to the Web service and is automatically passed the data that was stored in cache during the GetInitializer method.

ChainStream This method enables you to store the incoming SOAP message (in a Stream object) and create a new Stream object to hold output from the extension. During subsequent processing of the extension code, you should read data from the incoming stream and write data to the new output stream.

ProcessMessage This method performs the desired processing on the SOAP message. Typically, you will test the Stage property of the incoming message and use conditional logic in the procedure to determine the appropriate action to take. The Stage property will be one of the following: BeforeSerialize, AfterSerialize, BeforeDeserialize, AfterDeserialize.

The SOAP message is made available to your extension class in the ChainStream method (see Listing 8.3). Your code in the ChainStream method merely copies the incoming message into a Stream object (soapStream) and creates a new empty Stream object (myStream) to hold output. These Stream objects are declared as class-level variables so they will be available to all the methods of the SoapExtension class.

The ProcessMessage method is the most interesting because that is where you specify the custom code to be run and also at which stage it should be run. The base class version of ProcessMessage contains a Select Case statement that includes options for each of the stages that a SOAP message goes through as it is processed.

In Exercise 8.3, you will capture incoming messages in the BeforeDeserialize stage and capture the outgoing results in the AfterSerialize stage. These are the two stages where you can examine the XML markup of the SOAP message that is being transmitted. After you have determined the current message stage, you can call your own custom procedures (CopyStream and WriteStream) to create the log file entries. Listing 8.3 shows some of the code that you will use in Exercise 8.3 (for the full code listing, including the custom procedures, see the exercise).

Listing 8.3: The ChainStream and ProcessMessage Methods

start example
Private soapStream As Stream Private myStream As Stream Public Overrides Function ChainStream(ByVal  _    stream As Stream) As Stream    soapStream = stream    myStream = New MemoryStream()    Return myStream End Function Public Overrides Sub ProcessMessage(ByVal _    message As SoapMessage)    Select Case message.Stage       Case SoapMessageStage.BeforeDeserialize          CopyStream(soapStream, myStream)          WriteStream(Enviornment.NewLine & _             "***** Sent to Web service at " & _             Now.ToString & "*****" & Environment.NewLine)       Case SoapMessageStage.AfterDeserialize       Case SoapMessageStage.BeforeSerialize       Case SoapMessageStage.AfterSerialize          WriteStream(Environment.NewLine & _             "***** Returned from Web service at " & _             Now.ToString & "*****" & Environment.NewLine)          CopyStream(myStream, soapStream)    End Select End Sub
end example

To create the SoapExtensionAttribute class, you should override the ExtensionType and Priority properties. The code in Listing 8.4 shows these property procedures.

Listing 8.4: Properties of the SoapExtensionAttribute

start example
 Public Overrides ReadOnly Property ExtensionType() As Type    Get       Return GetType(DebugExtension)    End Get End Property Public Overrides Property Priority() As Integer    Get       Return m_Priority    End Get    Set(ByVal Value As Integer)       m_Priority = Value    End Set End Property
end example

The ExtensionType property returns the type of your derived SoapExtension class (called DebugExtension here). The Priority property determines the order in which multiple SOAP extensions would be processed (0 is the highest priority level). (Exercise 8.3 will also include another custom property to hold the filename for the log file.)

After you have created the classes derived from SoapExtension and SoapExtensionAttribute, you can compile them into a DLL. That DLL is then placed in the \bin directory of the XML Web service application that will use the SOAP extension.

When you create your XML Web service project, you will set a reference to the SOAP extension DLL. There are two ways to specify that the SOAP extension is to be invoked when the methods of the Web service are invoked. You can use an attribute to mark each method, as shown here:

<WebMethod(Description:="Get the square of a number"), _    DebugExtension.DebugExtension( _       LogFile:="C:\path\DebugInfo.txt", Priority:= "1")> _     Public Function GetSquare(ByVal _      inputVal As Double) As Double       Return inputVal * inputVal End Function

Or you can add the information to the web.config file:

<configuration>  <system.web>    <webServices>      <soapExtensionTypes>       <add type="DebugExtension.DebugExtension"            Priority="1"             LogFile="C:\path\DebugInfo.txt" />      <soapExtensionTypes>     <webServices>  <system.web> <configuration>

After you have marked your methods with the SOAP extension attribute, your custom code will be invoked each time a method of your Web service is called by a client, and again when the Web service sends a result back to the client.

Exercise 8.3 contains a comprehensive example of creating a SOAP extension that captures the XML markup of incoming and outgoing SOAP messages; you can extend the custom code in the class to log many different types of information about your Web service’s performance and usage. This exercise consists of three Visual Studio .NET projects:

  • A Class Library project that includes the SoapExtension and SoapExtensionAttribute classes

  • An ASP.NET Web service project

  • A Windows application project that will be used to test the Web service

Exercise 8.3: Using SOAP Extensions to Log SOAP Messages to a File

start example

Creating the SOAP Extension DLL:

  1. Start Visual Studio .NET and create a new Class Library project called DebugExtension.

  2. Remove the declaration for the default Class1.

  3. Set a reference to the System.Web.Services.dll and add the following Imports statements to the top of the module:

    Imports System.IO Imports System.Web.Services.Protocols
  4. Add two classes to this project, one that inherits from SoapExtension and one that inherits from SoapExtensionAttribute. In each of your derived classes, you will provide customized implementations of the base class methods. Your code for the DebugExtension class should look like this:

    Public Class DebugExtension    Inherits SoapExtension    Private soapStream As Stream    Private myStream As Stream    Private LogFile As String    'this initializer is used with a configuration file    Public Overloads Overrides Function GetInitializer( _       ByVal serviceType As System.Type) As Object          Return serviceType    End Function    'this initializer is used with an attribute    Public Overloads Overrides Function GetInitializer( _       ByVal methodInfo As LogicalMethodInfo, _       ByVal attribute As SoapExtensionAttribute) As Object          Return attribute    End Function    Public Overrides Sub Initialize(ByVal initializer As Object)       LogFile = CType(initializer, DebugExtensionAttribute).LogFile    End Sub    Public Overrides Function ChainStream(ByVal stream _       As Stream) As Stream       soapStream = stream       myStream = New MemoryStream()       Return myStream    End Function    Private Sub CopyStream(ByVal inputStream As Stream, ByVal outputStream As Stream)       Dim txtReader As TextReader = New StreamReader(inputStream)       Dim txtWriter As TextWriter = New StreamWriter(outputStream)       txtWriter.WriteLine(txtReader.ReadToEnd())       txtWriter.Flush()    End Sub    Private Sub WriteStream(ByVal title As String)       myStream.Position = 0       Dim myReader As New StreamReader(myStream)       Dim myWriter As New StreamWriter(LogFile, True)       myWriter.WriteLine(title)       myWriter.WriteLine(myReader.ReadToEnd)       myWriter.Close()       myStream.Position = 0    End Sub    Public Overrides Sub ProcessMessage(ByVal message As SoapMessage)       Select Case message.Stage          Case SoapMessageStage.BeforeDeserialize             CopyStream(soapStream, myStream)             WriteStream(Environment.NewLine & _                "***** Sent to Web service at " & _                Now.ToString & "*****" & Environment.NewLine)          Case SoapMessageStage.AfterDeserialize          Case SoapMessageStage.BeforeSerialize          Case SoapMessageStage.AfterSerialize             WriteStream(Environment.NewLine & _                "***** Returned from Web service at " & _                Now.ToString & "*****" & Environment.NewLine)             CopyStream(myStream, soapStream)       End Select    End Sub End Class 

  5. Now add the DebugExtensionAttribute class:

    <AttributeUsage(AttributeTargets.Method)> _  Public Class DebugExtensionAttribute     Inherits SoapExtensionAttribute     Private m_LogFile As String     Private m_Priority As Int32     Public Overrides ReadOnly Property ExtensionType() As Type         Get             Return GetType(DebugExtension)         End Get     End Property     Public Overrides Property Priority() As Integer         Get             Return m_Priority         End Get         Set(ByVal Value As Integer)             m_Priority = Value         End Set     End Property     Public Property LogFile() As String         Get             Return m_LogFile         End Get         Set(ByVal Value As String)             m_LogFile = Value         End Set     End Property End Class 

  6. Save your work. Build the DebugExtension class library.

    Creating the XML Web Service:

  7. Start Visual Studio .NET and create a new ASP.NET Web service application at http://localhost/DebugSOAP.

  8. Change the name of Service1.asmx to DebugService.asmx.

  9. View the code for DebugService.asmx and change the class name from Service1 to DebugService. Add an Imports statement:

    Imports System.Math
  10. Copy the DebugExtension.dll file to the \bin directory of the DebugSOAP project.

  11. Right-click the project name in the Solution Explorer and choose Add Reference from the menu. Click the Browse button and locate DebugExtension.dll in the project \bin directory. Click the Select button and then click OK.

  12. Create two Web methods for your class, similar to the ones that you created in Exercise 4.1 in Chapter 4, “Creating and Managing XML Web Services.” In addition to the basic function of these methods, you will add an additional attribute specifying the SOAP extension that will be invoked each time the Web method itself is invoked and the filename for the log file that should be used (use an appropriate path and filename for your computer).

  13. Your code should look like this:

    <WebMethod(Description:="Get the square of a number"), _    DebugExtension.DebugExtension( _       LogFile:="C:\path\DebugInfo.txt")> _     Public Function GetSquare(ByVal inputVal As Double) As Double       Return inputVal * inputVal End Function <WebMethod(Description:="Get the square root of a number"), _    DebugExtension.DebugExtension( _       LogFile:="C:\path\DebugInfo.txt")> _    Public Function GetSquareRoot(ByVal inputVal As Double) As Double       Return Sqrt(inputVal) End Function 

  14. Save your work and build the DebugSOAP project.

    Creating the Client Application:

  15. Create a new Windows application project named DebugExtensionClient.

  16. Add two TextBox controls—txtInputValue and txtResult—and two Command Button controls—btnGetSquare and btnGetSquareRoot. Your form design should look like the following.

    click to expand

  17. Right-click the project name in the Solution Explorer and choose Add Web Reference from the menu. In the Add Web Reference dialog box, type the URL for the Web service in the Address bar at the top of the dialog box:

    http://localhost/DebugSOAP/DebugService.asmx

    click to expand

  18. Create button click procedures for the two command buttons. In these procedures, you will call the GetSquare and GetSquareRoot methods of the Web service.

  19. Your code should look like this for btnGetSquare:

    Private Sub btnGetSquare_Click(ByVal sender As System.Object, _    ByVal e As System.EventArgs) Handles btnGetSquare.Click    Dim inputValue As Double = CType(txtInputValue.Text, Double)    Dim webResult As Double    Dim objSquare As localhost.DebugService = New localhost.DebugService()    webResult = objSquare.GetSquare(inputValue)    txtResult.Text = CType(webResult, String) End Sub 

  20. Your code should look like this for btnGetSquareRoot:

    Private Sub btnGetSquareRoot_Click(ByVal sender As System.Object, _    ByVal e As System.EventArgs) Handles btnGetSquareRoot.Click    Dim inputValue As Double = CType(txtInputValue.Text, Double)    Dim webResult As Double    Dim objSquare As localhost.DebugService = _       New localhost.DebugService()    webResult = objSquare.GetSquareRoot(inputValue)    txtResult.Text = CType(webResult, String) End Sub 

  21. Save and test your work. Run the application and type a value into txtInputValue. Click the Get Square button, then the Get Square Root button. You will see the results displayed in txtResult.

  22. Use Windows Explorer to locate the log file. The contents of the file should look something like this:

    ***** Sent to Web service at 3/23/2003 8:48:38 AM***** <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body>    <GetSquare xmlns="http://tempuri.org/">       <inputVal>4</inputVal>    </GetSquare> </soap:Body> </soap:Envelope> ***** Returned from Web service at 3/23/2003 8:48:38 AM***** <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body>    <GetSquareResponse xmlns="http://tempuri.org/">       <GetSquareResult>16</GetSquareResult>    </GetSquareResponse> </soap:Body> </soap:Envelope>

end example



MCAD/MCSD(c) Visual Basic. NET XML Web Services and Server Components Study Guide
MCAD/MCSD: Visual Basic .NET XML Web Services and Server Components Study Guide
ISBN: 0782141935
EAN: 2147483647
Year: 2005
Pages: 153

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net