Introduction
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 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 to the web service. In case you are not aware of generating a WSDL file of a Web Service, then follow the steps mentioned below:
- Type in your Web Service URL along with the ASMX file in the web browser.
- 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.
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
The following image displays a sample SOAP Request and 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 a 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 to that service.
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 too.
To have some flexibilities, few properties are created in the TraceExtensionAttribute
which can be passed as parameters in the constructor.
LogTypeMode
– This denotes which SOAP XML to log – None
, RequestOnly
, ResponseOnly
, RequestReponse
ReqFileName
– The file name where Request SOAP XML will be logged 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:
ResponseNamespace=http:
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.
- When a web method in the client proxy is called, the framework checks if any SOAP extensions are to be invoked or not and if so, then those are called with
BeforeSerialize
stage. - Once the serialization is done, then the extensions are called with
AfterSerialize
stage. - The SOAP message is sent to the server and server figures out which method to route to.
- 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. - The server deserializes the stream and invokes all the extensions for the
AfterDeserialize
stage. - After the web method execution server invokes all the extensions with
BeforeSerialize
stage. - Once the server serializes the result stream, the SOAP extensions are called with
AfterSerialize
stage. - Result is sent back to client
- Now client receives the result stream and framework invokes the SOAP extensions with
BeforeDeserialize
event stage. - 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, it 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"/>
</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
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.