Creating and Using SOAP Extensions

   


Create and Consume an XML Web service: Create and use SOAP extensions.

SOAP extensions allow you to perform your own processing on SOAP messages that pass between a Web service client and a Web service server. A SOAP extension can perform tasks such as encryption, signature verification, translation, or any other modification of the SOAP messages being passed. In this section of the chapter, you'll learn the mechanics of writing and using a SOAP extension.

The Extensible Web Services Architecture

To understand how SOAP extensions fit into Web services, it's helpful to have an understanding of the general Web services architecture. Figure 5.1 shows schematically how Web services clients and servers communicate with one another.

Figure 5.1. Web service communication.

When a Web service client invokes a Web method from a Web service server, the client takes the objects and parameters involved and turns them into an XML message, the SOAP request. This process of converting objects to XML is called serialization. On the server end, the reverse process (called deserialization) is used to turn the XML back into objects. The server then sends back the SOAP response, which goes through a similar process of serialization and deserialization.

SOAP extensions allow you to run your own code as part of the serialization or deserialization process. Figure 5.2 shows schematically how SOAP extensions fit into the Web services architecture.

Figure 5.2. Web service communication with SOAP extensions.

In Figure 5.2, the circles show the points at which you can insert a SOAP extension into the Web service architecture. As you can see, SOAP extensions are quite flexible: You can run code in a SOAP extension before or after a serialization or deserialization operation.

EXAM TIP

Multiple SOAP Extensions It's perfectly possible to have more than one SOAP extension working with a single SOAP message. You'll see later on that you can configure SOAP extensions with a priority. The SOAP extension with the lowest priority modifies the message first; the SOAP extension with the highest priority modifies the message last.


Soap extensions can be deployed on the client or on the server, and singly or in pairs. Here are some examples to suggest how particular types of SOAP extensions might fit into the overall Web services picture:

  • A SOAP extension designed to measure overall server traffic for a Web service would be deployed only on the server. Clients would never know anything about this SOAP extension.

  • A SOAP extension designed to translate requests from English measurements to metric measurements, as well as responses from metric measurements to English measurements, could be deployed only on the client. As far as the server knows , it's still dealing with a client making requests in metric measurements.

  • A SOAP extension designed to digitally sign messages, as well as to verify the digital signature on delivery, would need to be deployed on both the client and the server. Each side would sign outgoing messages and verify incoming signatures.

Writing a Server-Side SOAP Extension

Writing a SOAP extension is one of the more difficult Web service tasks that you're likely to tackle for this exam. Quite a bit of intricate code is involved in addition to the code that you'll need to implement the Web service itself. But if you examine each piece of code carefully , you'll see how it all fits together.

In Step By Step 5.1, you'll put together a Web service that includes a SOAP extension. This SOAP extension will log incoming and outgoing messages to a disk file. Before getting started, it might help to have a high-level overview of the tasks that the SOAP extension code must perform:

  1. Derive a new class from the SoapExtension class.

  2. Implement the GetInitializer and Initialize methods to handle startup chores.

  3. Implement the ChainStream method to intercept the SOAP messages being serialized and deserialized.

  4. Implement the ProcessMessage method to work with the SOAP messages.

  5. Derive a new class from the SoapExtensionAttribute class to mark methods that will use the new extension.

You'll see each of these tasks in Step By Step 5.1.

STEP BY STEP

5.1 Creating a Server-Side SOAP Extension

  1. Create a new Visual Basic ASP.NET Web Service application in the Visual Studio .NET IDE. Name the new Web service BugTracker.

  2. Right-click the Service1.asmx in Server Explorer and select Rename. Change the name of this file to Bugs.asmx.

  3. Right-click the Bugs.asmx file in Server Explorer and select View Code. Change the name of the class in the class declaration from Service1 to Bugs. Change the Namespace for the class in the WebService attribute to http://BugTracker.que.com/ .

  4. Add a new class file to the project. Name the new class Bug.vb. Modify the Bug.vb class to contain this code:

     Public Class Bug     Private m_BugID As Integer     Private m_Description As String     Public Sub New()         m_Description = ""     End Sub     Public Sub New(ByVal BugID As Integer)         m_BugID = BugID         ' Test data         ' A real implementation would retrieve details         ' from a database or other store here         m_Description = "Initializing the " & _          "thingamabob causes the whatsit to crash."     End Sub     Public Property BugID() As Integer         Get             BugID = m_BugID         End Get         Set(ByVal Value As Integer)             m_BugID = BugID         End Set     End Property     Public Property Description() As String         Get             Description = m_Description         End Get         Set(ByVal Value As String)             m_Description = Description         End Set     End Property End Class 
  5. Add a Web Method to the Bugs.asmx.vb file to return a Bug object when passed a Bug ID from the client:

     <WebMethod()> _ Public Function GetBug(ByVal BugID As Integer) As Bug     Dim b As Bug = New Bug(BugID)     GetBug = b End Function 
  6. Add a new class to the project. Name the new class SoapDisplayExtension.vb. This class will implement a SOAP extension that writes incoming and outgoing messages to a file.

  7. Add these directives at the top of the SoapDisplayExtension class:

     Imports System.IO Imports System.Text Imports System.Web.Services Imports System.Web.Services.Protocols 
  8. Add a line of code to make this class a subclass of the SoapExtension class:

     Public Class SoapDisplayExtension     Inherits SoapExtension 
  9. Add two variables to the class. These variables will store the original SOAP message (passed to your SOAP extension as a stream object) and the possibly modified SOAP message from your extension to the next stage of processing:

     ' Variables to hold the original stream ' and the stream that we return Private originalStream As Stream Private internalStream As Stream 
  10. Add code for the GetInitializer and Initialize methods. Although these methods are not used in this particular example, you still need to implement them because they are marked as MustOverride methods in the base class:

     ' Called the first time the Web service is used ' Version called if configured with an attribute Public Overloads Overrides Function GetInitializer(_  ByVal methodInfo As LogicalMethodInfo, _  ByVal attribute As SoapExtensionAttribute) As Object     ' Not used in this example, but it's declared     ' MustOverride in the base class End Function ' Version called if configured with a config file Public Overloads Overrides Function GetInitializer(_ ByVal WebServiceType As Type) As Object     ' Not used in this example, but it's declared     ' MustOverride in the base class End Function ' Called each time the Web service is used ' And gets passed the data from GetInitializer Public Overrides Sub Initialize(_  ByVal initializer As Object)     ' Not used in this example, but it's declared     ' MustOverride in the base class End Sub 

    EXAM TIP

    GetInitializer and Initialize The GetInitializer method is called once when your SOAP extension is first initialized . There are two forms of this method, depending on whether the SOAP extension was configured with an attribute or with a configuration file. You can pass any object you like as the return value from the GetInitializer method. The Initialize method is called once for every SOAP message serialized or deserialized by the Web service. The object passed to the Initialize method is the same object that you return from the GetInitializer method. So, for example, you might open a file in the GetInitializer method and pass a filestream object as the return value from that method to become the input parameter to the Initializes method.

  11. Add code to implement the ChainStream method. This method allows you to intercept the stream that the Web service is working with and substitute your own stream in its place:

     ' The Chainstream method gives us a chance ' to grab the SOAP messages as they go by Public Overrides Function ChainStream(_  ByVal stream As Stream) As Stream     ' Save the original stream     originalStream = stream     ' Create and return our own in its place     internalStream = New MemoryStream()     ChainStream = internalStream End Function 

    NOTE

    Overloads and Overrides The Overloads keyword indicates that two methods have the same name but different parameter signatures so that the .NET Runtime can decide which version of the method to call based on the passed arguments. The Overrides keyword indicates that a method in a derived class has the same signature as a method in the base class and that the derived class method should be called by the runtime instead of the base class method.

  12. Add code to handle SOAP messages in the ProcessMessage method. Note that this method is called in four different situations (before and after serialization and deserialization), and that you can use the passed parameter to determine which stage you're in:

     ' The ProcessMessage method is where we do our work Public Overrides Sub ProcessMessage(_  ByVal message As SoapMessage)     ' Determine the stage and take appropriate action     Select Case message.Stage         Case SoapMessageStage.BeforeSerialize             ' About to prepare a SOAP Response         Case SoapMessageStage.AfterSerialize            ' SOAP response is all prepared            ' Open a log file and write a status line            Dim fs As FileStream = _             New FileStream("c:\temp\BugTracker.log", _             FileMode.Append, FileAccess.Write)            Dim sw As New StreamWriter(fs)            sw.WriteLine("AfterSerialize")            sw.Flush()            ' Copy the passed message to the file            internalStream.Position = 0            CopyStream(internalStream, fs)            fs.Close()            ' Copy the passed message            ' to the other stream            internalStream.Position = 0            CopyStream(internalStream, originalStream)            internalStream.Position = 0         Case SoapMessageStage.BeforeDeserialize             ' About to handle a SOAP request             ' Copy the passed message             ' to the other stream             CopyStream(originalStream, internalStream)             internalStream.Position = 0             ' Open a log file and write a status line             Dim fs As FileStream = _              New FileStream("c:\temp\BugTracker.log", _              FileMode.Append, FileAccess.Write)             Dim sw As New StreamWriter(fs)             sw.WriteLine("BeforeDeserialize")             sw.Flush()             ' Copy the passed message to the file             CopyStream(internalStream, fs)             fs.Close()             internalStream.Position = 0         Case SoapMessageStage.AfterDeserialize             ' SOAP request has been deserialized     End Select End Sub ' Helper function to copy one stream to another Private Sub CopyStream(ByVal fromStream As Stream, _ ByVal toStream As Stream)     Try         Dim sr As New StreamReader(fromStream)         Dim sw As New StreamWriter(toStream)         sw.WriteLine(sr.ReadToEnd())         sw.Flush()     Catch ex As Exception     End Try End Sub 

    EXAM TIP

    Streams, Serialization, and Deserialization If your SOAP extension is called during serialization, the internal variable that you returned will contain the SOAP message, and you should copy it to the original variable before you finish processing. If your SOAP extension is called during deserialization, the original variable will contain the SOAP message, and you should copy it to the internal variable before you finish processing. And remember, the client serializes requests and deserializes responses, whereas the server deserializes requests and serializes responses.

  13. Add a new class file to the project. Name the new class SoapDisplayExtensionAttribute.vb. Modify the SoapDisplayExtensionAttribute.vb class to contain this code:

     Imports System.Web.Services Imports System.Web.Services.Protocols <AttributeUsage(AttributeTargets.Method)> _ Public Class SoapDisplayExtensionAttribute     Inherits SoapExtensionAttribute     Private m_Priority As Integer = 1     ' Specifies the class of the SOAP     ' Extension to use with this method     Public Overrides ReadOnly Property _      ExtensionType() _      As Type         Get             ExtensionType = _              GetType(SoapDisplayExtension)         End Get     End Property     ' Member to store the extension's priority     Public Overrides Property Priority() As Integer         Get             Priority = m_Priority         End Get         Set(ByVal Value As Integer)             m_Priority = Value         End Set     End Property End Class 

    EXAM TIP

    Attribute Naming Visual Basic .NET will add an implied "Attribute" to the name of any attribute if necessary. Thus, to apply the SoapDisplayExtensionAttribute to any method, you can refer to it by its full name or simply as SoapDisplayExtension.

  14. Modify the declaration of the Web method in the Bugs.asmx.vb class to include the SoapDisplayExtensionAttribute. This is what tells the CLR that it should hook your SOAP extension in to this Web method:

     <SoapDisplayExtension(), WebMethod()> _ Public Function GetBug(ByVal BugID As Integer) As Bug     Dim b As Bug = New Bug(BugID)     GetBug = b End Function 

NOTE

Alternative Configuration Techniques There are two different ways to attach a SOAP extension to a particular Web method. You can use an attribute, as demonstrated in this Step By Step, or you can make entries into the XML configuration file for the project. You'll see this second method demonstrated in Exercise 5.1.


When a request comes in for the GetBug method, the Web service sees the SoapDisplayExtension attribute and routes the request and response through the SoapDisplayExtension class. Within that class, the SOAP messages are logged to a disk file and then passed on. In Step By Step 5.2, you'll build a client project to confirm that this works.

STEP BY STEP

5.2 Testing a Server-Side SOAP Extension

  1. Create a new Visual Basic .NET Windows Application in the Visual Studio .NET IDE.

  2. Right-click the References folder in Solution Explorer and select Add Web Reference. This will open the Add Web reference dialog box.

  3. Type http:// YourServerName /BugTracker/Bugs.asmx (substituting your own Web server name) into the Address bar of the Add Web Reference dialog box and press Enter. This connects to the server and downloads the information about the BugTracker Web service. Click the Add Reference button to add the information to the project.

  4. Add a new form to the project. Name the new form StepByStep5-2.vb.

  5. Place a Button control named btnGetBug and a TextBox control named txtDescription on the form. Set the Multiline property of the txtDescription control to True .

  6. Double-click the Button control to open the form's module. Add this code to handle the Button's Click event:

     Private Sub btnGetBug_Click(_  ByVal sender As System.Object, _  ByVal e As System.EventArgs) Handles btnGetBug.Click     Dim B As localhost.Bugs = New localhost.Bugs()     Dim b1 As localhost.Bug = B.GetBug(1)     txtDescription.Text = b1.Description End Sub 
  7. Set the form as the startup object for the project.

  8. Run the project and click the button on the form. You should see the bug description, as shown in Figure 5.3.

    Figure 5.3. Testing a Web service.

  9. On the Web server where the Web service is located, open the file c:\Temp\BugTracker.log. This file will contain both the incoming SOAP request and the outgoing SOAP response, as saved by the SOAP extension. I've reformatted the file contents here for easier reading:

     BeforeDeserialize <?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>     <GetBug xmlns="http://BugTracker.que.com/">       <BugID>1</BugID>     </GetBug>   </soap:Body> </soap:Envelope> AfterSerialize <?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>    <GetBugResponse xmlns="http://BugTracker.que.com/">       <GetBugResult>         <BugID>1</BugID>         <Description>Initializing           the thingamabob causes           the whatsit to crash.</Description>       </GetBugResult>     </GetBugResponse>   </soap:Body> </soap:Envelope> 

Writing a Client-Side SOAP Extension

SOAP Extensions can also, as I said, run on the client. In Step by Step 5.3, you'll develop a version of the SoapDisplayExtension class to run on the client. You'll see that the client SOAP extension is very similar to the server SOAP extension. However, you can take advantage of Windows forms to display the information as it's received, rather than dumping it to a log file.

STEP BY STEP

5.3 Creating a Client-Side SOAP Extension

  1. Add a new form to the project that you created in Step By Step 5.2. Name the new form DisplayMessage.vb. Set the form's Text property to SOAP Message . Add a textbox named txtMessage to the form and set its Multiline property to True .

  2. Add a new XSLT File to the project. Name the new XSLT file identity.xslt and edit its contents as follows :

     <?xml version = "1.0"?> <xsl:stylesheet xmlns:   xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">   <xsl:output method="xml" indent="yes"/>   <xsl:template match="node()@*">     <xsl:copy>       <xsl:apply-templates select="node()@*"/>     </xsl:copy>   </xsl:template> </xsl:stylesheet> 
  3. Add a new class to the project. Name the new class SoapDisplayExtension.vb. This class will implement a SOAP extension that writes incoming and outgoing messages to a file.

  4. Add these directives at the top of the SoapDisplayExtension class:

     Imports System.IO Imports System.Web.Services Imports System.Web.Services.Protocols Imports System.Xml Imports System.Xml.Xsl 
  5. Add a line of code to make this class a subclass of the SoapExtension class:

     Public Class SoapDisplayExtension     Inherits SoapExtension 
  6. Add two variables to the class. These variables will store the original SOAP message (passed to your SOAP extension as a stream object) and the possibly modified SOAP message from your extension to the next stage of processing:

     ' Variables to hold the original stream ' and the stream that we return Private originalStream As Stream Private internalStream As Stream 
  7. Add code for the GetInitializer and Initialize methods. Although these methods are not used in this particular example, you still need to implement them because they are marked as MustOverride methods in the base class:

     ' Called the first time the Web service is used ' Version called if configured with an attribute Public Overloads Overrides Function GetInitializer(_  ByVal methodInfo As LogicalMethodInfo, _  ByVal attribute As SoapExtensionAttribute) As Object     ' Not used in this example, but it's declared     ' MustOverride in the base class End Function ' Version called if configured with a config file Public Overloads Overrides Function GetInitializer(_ ByVal WebServiceType As Type) As Object     ' Not used in this example, but it's declared     ' MustOverride in the base class End Function ' Called each time the Web service is used ' And gets passed the data from GetInitializer Public Overrides Sub Initialize(_  ByVal initializer As Object)     ' Not used in this example, but it's declared     ' MustOverride in the base class End Sub 
  8. Add code to implement the ChainStream method. This method allows you to intercept the stream that the Web service is working with and substitute your own stream in its place:

     ' The Chainstream method gives us a chance ' to grab the SOAP messages as they go by Public Overrides Function ChainStream(_  ByVal stream As Stream) As Stream     ' Save the original stream     originalStream = stream     ' Create and return our own in its place     internalStream = New MemoryStream()     ChainStream = internalStream End Function 
  9. Add code to handle SOAP messages in the ProcessMessage method:

     ' The ProcessMessage method is where we do our work Public Overrides Sub ProcessMessage(_  ByVal message As SoapMessage)     ' Determine the stage and take appropriate action     Select Case message.Stage         Case SoapMessageStage.BeforeSerialize             ' About to prepare a SOAP request         Case SoapMessageStage.AfterSerialize             ' SOAP request is all prepared             internalStream.Position = 0             ' Get a Transform ready             Dim xslt As XslTransform = _              New XslTransform()             xslt.Load("..\identity.xslt")             ' Load the raw XML into an XML document             Dim xd As XmlDocument = New XmlDocument()             xd.Load(internalStream)             ' Use the transform to pretty print it            Dim ms As MemoryStream = New MemoryStream()             xslt.Transform(xd, Nothing, ms)             ' And drop the results to a TextBox control             ms.Position = 0             Dim sr As StreamReader = _              New StreamReader(ms)             Dim f As New DisplayMessage()             f.txtMessage.Text = sr.ReadToEnd()             f.Show()             ' Copy the passed message             ' to the other stream             internalStream.Position = 0             CopyStream(internalStream, originalStream)             internalStream.Position = 0         Case SoapMessageStage.BeforeDeserialize             ' About to handle a SOAP response             ' Copy the passed message             ' to the other stream             CopyStream(originalStream, internalStream)             internalStream.Position = 0             ' Get a Transform ready             Dim xslt As XslTransform = _              New XslTransform()             xslt.Load("..\identity.xslt")             ' Load the raw XML into an XML document             Dim xd As XmlDocument = New XmlDocument()             xd.Load(internalStream)             ' Use the transform to pretty print it             Dim ms As MemoryStream = New MemoryStream()             xslt.Transform(xd, Nothing, ms)             ' And drop the results to a TextBox control             ms.Position = 0             Dim sr As StreamReader = _              New StreamReader(ms)             Dim f As New DisplayMessage()             f.txtMessage.Text = sr.ReadToEnd()             f.Show()             internalStream.Position = 0         Case SoapMessageStage.AfterDeserialize             ' SOAP response has been deserialized     End Select End Sub ' Helper function to copy one stream to another Private Sub CopyStream(ByVal fromStream As Stream, _ ByVal toStream As Stream)     Try         Dim sr As New StreamReader(fromStream)         Dim sw As New StreamWriter(toStream)         sw.WriteLine(sr.ReadToEnd())         sw.Flush()     Catch ex As Exception     End Try End Sub 

    NOTE

    XSL Transforms This code uses an XSL transform to "pretty print" the SOAP messages with indenting to make them easier to read. For more information on XSL transforms in .NET, see the documentation for the System.Xml.Xsl namespace.

  10. Add a new class file to the project. Name the new class SoapDisplayExtensionAttribute.vb. Modify the SoapDisplayExtensionAttribute.vb class to contain this code:

     Imports System.Web.Services Imports System.Web.Services.Protocols <AttributeUsage(AttributeTargets.Method)> _ Public Class SoapDisplayExtensionAttribute     Inherits SoapExtensionAttribute     Private m_Priority As Integer = 1     ' Specifies the class of the SOAP     ' Extension to use with this method     Public Overrides ReadOnly Property _      ExtensionType() _      As Type         Get             ExtensionType = _              GetType(SoapDisplayExtension)         End Get     End Property     ' Member to store the extension's priority     Public Overrides Property Priority() As Integer         Get             Priority = m_Priority         End Get         Set(ByVal Value As Integer)             m_Priority = Value         End Set     End Property End Class 
  11. Click the Show All Files button on the Solution Explorer toolbar. Expand the Web References folder to find the Reference.vb file, as shown in Figure 5.4. This is the proxy class that was created when you set the Web reference in the project. Double-click the Reference.vb file to open it in the editor.

    Figure 5.4. Locating the automatically generated proxy class.

  12. Locate the GetBug function in the proxy class and modify it by adding the SoapDisplayExtension attribute to the class declaration, as follows:

     <SoapDisplayExtension(), _  System.Web.Services.Protocols. _  SoapDocumentMethodAttribute(_  "http://BugTracker.que.com/GetBug", _  RequestNamespace:="http://BugTracker.que.com/", _  ResponseNamespace:="http://BugTracker.que.com/", _  Use:=System.Web.Services.Description. _  SoapBindingUse.Literal, _  ParameterStyle:= _  System.Web.Services.Protocols. _  SoapParameterStyle.Wrapped)> _ Public Function GetBug(ByVal BugID As Integer) As Bug     Dim results() As Object = _      Me.Invoke("GetBug", New Object() {BugID})     Return CType(results(0), Bug) End Function 
  13. Run the project. Click the button on the default form. The project will retrieve information from the Web service and will display both the SOAP request and the SOAP response on separate instances of the DisplayMessage form, as shown in Figure 5.5.

    Figure 5.5. SOAP messages displayed by a client-side SOAP extension.

REVIEW BREAK

  • A SOAP extension allows you to intercept SOAP messages at all stages of processing: before and after serialization and before and after deserialization.

  • SOAP extensions are classes that are derived from the base SoapExtension class.

  • SOAP extensions can run on a Web services client, server, or both.

  • The GetInitializer method is called once when the SOAP extension is first loaded.

  • The Initialize method is called once for each call to a Web method being monitored by the SOAP extension.

  • The ChainStream method allows you to intercept the actual SOAP message stream.

  • The ProcessMessage method is called with the actual SOAP requests and responses.


   
Top


MCAD. MCSD Training Guide (Exam 70-310. Developing XML Web Services and Server Components with Visual Basic. NET and the. NET Framework)
MCAD/MCSD Training Guide (70-310): Developing XML Web Services and Server Components with Visual Basic(R) .NET and the .NET Framework
ISBN: 0789728206
EAN: 2147483647
Year: 2002
Pages: 166

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