Arindam Sinha Online

August 8, 2009

Trace SOAP Request/Response XML with TraceExtension

Filed under: .NET,ASP.NET,Microsoft,SOAP,Web Service — Arindam Sinha @ 8:52 pm
Tags: , , ,

In one of my recent projects, I had to interact with one .NET Web Service (ASMX) from a Windows Desktop application. Now another java application would be accessing the same Web Service. So the java developers need a sample SOAP Request/Response XML for the Web Service. Before I start discussing on the approach of logging SOAP Request-Response XML, I would like to elaborate on same basic things Web Service referencing.

What’s WSDL File and how it’s generated?

Web Services Description Language is an XML format for describing network services as a set of endpoints operating on messages containing either document-oriented or procedure-oriented information. In a single sentence – it defines the Web Service which can be used by the clients to subscribe the web service.In case you are not aware of generating a WSDL file of a Web Service, then follow the steps mentions below –

  1. Type in your Web Service URL along with the ASMX file in the web browser.
  2. Then just append “?wsdl” at the end of URL your required WSDL will be generated in the browser. So if your URL is http://localhost/HelloWorldWebService/HelloWorldService.asmx then if you type in http://localhost/HelloWorldWebService/HelloWorldService.asmx?wsdl to generate the wsdl for your web service.

How to reference a Web Service?

This is quite easy for Visual Studio users. Using the wizard of Add Web Reference (as shown below in the images), the developer just needs to provide the URL of the Web Service. This would generate the proxy and the developer does not even need to create the WSDL separately.

Add Web Reference

Web Reference URL

 

Another approach could be generation of proxy class from WSDL file by using the WSDL.exe. Once proxy is generated, proxy class could be used for interaction with the Web Service.

SOAP and SOAP XML

Simple Object Access Protocol is a simple XML-based protocol to let applications exchange information over HTTP. SOAP is a protocol for accessing a Web Service.

A SOAP message is an ordinary XML document containing the following elements:

  • An Envelope element that identifies the XML document as a SOAP message
  • A Header element that contains header information
  • A Body element that contains call and response information
  • A Fault element containing errors and status information

Following image displays a sample SOAP Request and Response XML.

Sample SOAP Request Response XML

What’s SOAP Extension – TraceExtension?

SOAP Extensions allow developers to create very interesting applications on top of the core SOAP architecture found within .NET. It allows developers to implement encryption algorithm, compression routine, SOAP attachments etc. In this article we will focus on logging the SOAP request/response XML.

For this we need to create couple of classes –

  • Create a class that derives from System.Web.Services.Protocols.SoapExtension – TraceExtension class has been created for this
  • Create a class that derives from System.Web.Services.Protocols.SoapExtensionAttribute – TraceExtensionAttribute class has been created for this

A sample Web Service application

To understand the functionality of TraceExtension class, I will take a sample WebService and a client which subscribes that service.

  • HelloWorldWebService – This is a web service with a web method HelloWorld.
public class HelloWorldService : System.Web.Services.WebService
{

    [WebMethod]
    public string HelloWorld(string myString)
    {
        return "Hello World Service returns - " + myString;
    }
}
  • ClientSubscriber – This is a simple windows form application consuming the web reference of HelloWorldWebService. In the form, there’s a textbox and button. On click of this button, the text of the textbox will send to the web service and the result will displayed in a message box.
  • SoapMessageUtility – This utility has the actual implementation of TraceExtension. The client application has a reference to this utility to log the SOAP request/response XML.

The sample source code and binaries are available here.

Implementation of TraceExtension

We will implement the TraceExtension in the client side consuming the web service. So the Request/Response SOAP XML logs will be created in the client side.

First we will take look at the TraceExtensionAttribute class inherited from SoapExtensionAttribute. TraceExtensionAttribute must override the ExtensionType property to return the type of extension that is associated with the attribute. In this case this is nothing but TraceExtension.

public override Type ExtensionType
{
    get { return typeof(TraceExtension); }
}

For setting the priority of the ‘SoapExtension’, Priority property has been overridden also.

To have some flexibilities, few properties are created in the TraceExtensionAttribute which can be passed as parameters in the constructor.

  1. LogTypeMode – This denotes which SOAP XML to log – None, RequestOnly, ResponseOnly, RequestReponse.
  2. ReqFileName – The file name where Request SOAP XML will be logged
  3. ResFileName – The file name where Response SOAP XML will be logged

TraceExtensionAttribute class can be used at method level as shown below.

[TraceExtensionAttribute(LogType.RequestReponse,"C:\\Log\\MyReq.log","C:\\Log\\MyRes.log")]
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/HelloWorld", RequestNamespace="http://tempuri.org/", ResponseNamespace="http://tempuri.org/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
public string HelloWorld(string myString) {
    object[] results = this.Invoke("HelloWorld", new object[] {
                myString});
    return ((string)(results[0]));
}

Now we will look into the TraceExtension class inherited from SoapExtension. The core piece of implementation is the ProcessMessage method of the SoapExtension class. This method should be overridden to have the proper implementation. Before this implementation we should be very much aware of the flow of SOAP message from the client to Web Service.

  1. When a web method in the client proxy is called, framework checks if any SOAP extensions to be invoked or not and if so, then those are called with BeforeSerialize stage.
  2. Once the serialization is done, then the extensions are called with AfterSerialize stage.
  3. The SOAP message is sent to the server and server figures out which method to route to.
  4. Then the server checks to see if any SOAP extensions (that’s us!) should be invoked, and if so, invokes them with the BeforeDeserialize event stage.
  5. The server deserializes the stream and invokes all the extensions for the AfterDeserialize stage.
  6. After the web method execution server invokes all the extensions with BeforeSerialize stage.
  7. Once the server serializes the result stream, the SOAP extensions are called with AfterSerialize stage.
  8. Result is sent back to client
  9. Now client receives the result stream and framework invokes the SOAP extensions with BeforeDeserialize event stage.
  10. Once client deserializes the result stream, SOAP extensions with AfterDeserialize stage is invoked.

In our case, we will be focused in the client side i.e. points 1,2,9 and 10. 

The key is the AfterSerialize stage (i.e. point 2) in the client, the request SOAP XML can be logged at this moment.

public void WriteOutput(SoapMessage message)
{
    FileStream fs;
    StreamWriter w = null; 
    
    try
    {
        if (_logTypeMode.Equals(LogType.RequestOnly) || _logTypeMode.Equals(LogType.RequestReponse))
        {
            _newStream.Position = 0;
            fs = new FileStream(_reqFilename, FileMode.Append,
                FileAccess.Write);
            w = new StreamWriter(fs);

            string soapString = "SoapRequest";
            w.WriteLine("-----" + soapString + " at " + DateTime.Now);
            w.Flush();
            Copy(_newStream, fs);
            w.Close();

        }
    }
    catch (Exception ex)
    {
    }
    finally
    {
        if (w != null)
            w.Close();
        _newStream.Position = 0;
        Copy(_newStream, _oldStream);
    }
}

The same is applicable for the BeforeDeserialize stage (i.e. point 9) to log the response SOAP XML. The WriteInput method logs the response XML.

Safety and Configurability

The whole purpose of this article is to log the request-response SOAP XML. In case of any exception while logging would cause interruption in the web service invocation. So the WriteInput and WriteOutput methods in TraceExtension class should take care of proper exception handling. In my case I kept a blank catch block which can be modified with some exception logging mechanism but that should not impact the normal web service invocation.

Already I tried to provide some flexibilities in the TraceExtensionAttribute with few properties. These can be set while instantiating the attribute in the method level. Also, TraceExtension class can support the following entries in (config files web.config/app.config).

<appSettings>
  <add key ="REQ_LOGFILE" value="c:\log\SOAPReq_log.txt"/>
  <add key ="RES_LOGFILE" value="c:\log\SOAPRes_log.txt"/>

  <add key ="LogTypeMode" value="3"/>
<!-- None = 0,
      RequestOnly = 1,
      ResponseOnly = 2,
      RequestReponse = 3 -->
</appSettings>

If both config entries and attribute parameters are provided, then config entries would override that. So my utility (i.e. SoapMessageUtility.dll) can be directly used with the above configuration entries.

References

You can download the code and binaries by clicking here. I would suggest you all to have a look into the MSDN link on this. Also for beginners to SOAP, the tutorial from w3schools might be helpful.

Create a free website or blog at WordPress.com.