Reputation: 34208
In my solution I have three projects. One is a WCF service, another is a Winforms app from where I am calling the web service, and the last is a class library where class has been design extending soap extension class.
My objective is to capture response & request xml when I am calling the WCF service from my Winforms app. I am getting object reference not set error when I am trying to capture xml.
Here is my class library source code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.IO;
using System.Net;
using System.Xml;
namespace SoapLogger
{
public class TraceExtension : SoapExtension
{
private Stream oldStream;
private Stream newStream;
private static XmlDocument xmlRequest;
/// <summary>
/// Gets the outgoing XML request sent to PayPal
/// </summary>
public static XmlDocument XmlRequest
{
get { return xmlRequest; }
}
private static XmlDocument xmlResponse;
/// <summary>
/// Gets the incoming XML response sent from PayPal
/// </summary>
public static XmlDocument XmlResponse
{
get { return xmlResponse; }
}
/// <summary>
/// Save the Stream representing the SOAP request
/// or SOAP response into a local memory buffer.
/// </summary>
/// <param name="stream">
/// <returns></returns>
public override Stream ChainStream(Stream stream)
{
oldStream = stream;
newStream = new MemoryStream();
return newStream;
}
/// <summary>
/// If the SoapMessageStage is such that the SoapRequest or
/// SoapResponse is still in the SOAP format to be sent or received,
/// save it to the xmlRequest or xmlResponse property.
/// </summary>
/// <param name="message">
public override void ProcessMessage(SoapMessage message)
{
switch (message.Stage)
{
case SoapMessageStage.BeforeSerialize:
break;
case SoapMessageStage.AfterSerialize:
xmlRequest = GetSoapEnvelope(newStream);
CopyStream(newStream, oldStream);
break;
case SoapMessageStage.BeforeDeserialize:
CopyStream(oldStream, newStream);
xmlResponse = GetSoapEnvelope(newStream);
break;
case SoapMessageStage.AfterDeserialize:
break;
}
}
/// <summary>
/// Returns the XML representation of the Soap Envelope in the supplied stream.
/// Resets the position of stream to zero.
/// </summary>
/// <param name="stream">
/// <returns></returns>
private XmlDocument GetSoapEnvelope(Stream stream)
{
XmlDocument xml = new XmlDocument();
stream.Position = 0;
StreamReader reader = new StreamReader(stream);
xml.LoadXml(reader.ReadToEnd());
stream.Position = 0;
return xml;
}
/// <summary>
/// Copies a stream.
/// </summary>
/// <param name="from">
/// <param name="to">
private void CopyStream(Stream from, Stream to)
{
TextReader reader = new StreamReader(from);
TextWriter writer = new StreamWriter(to);
writer.WriteLine(reader.ReadToEnd());
writer.Flush();
}
#region NoOp
/// <summary>
/// Included only because it must be implemented.
/// </summary>
/// <param name="methodInfo">
/// <param name="attribute">
/// <returns></returns>
public override object GetInitializer(LogicalMethodInfo methodInfo,
SoapExtensionAttribute attribute)
{
return null;
}
/// <summary>
/// Included only because it must be implemented.
/// </summary>
/// <param name="WebServiceType">
/// <returns></returns>
public override object GetInitializer(Type WebServiceType)
{
return null;
}
/// <summary>
/// Included only because it must be implemented.
/// </summary>
/// <param name="initializer">
public override void Initialize(object initializer)
{
}
#endregion NoOp
}
Here is how I am calling the web service from my Winforms app:
private void button1_Click(object sender, EventArgs e)
{
using (ServiceRef.TestServiceSoapClient oService = new ServiceRef.TestServiceSoapClient())
{
textBox1.Text = oService.HelloWorld("Sudip");
var soapRequest = SoapLogger.TraceExtension.XmlRequest.InnerXml;
var soapResponse = SoapLogger.TraceExtension.XmlResponse.InnerXml;
}
}
These two lines are causing the object reference error
var soapRequest = SoapLogger.TraceExtension.XmlRequest.InnerXml;
var soapResponse = SoapLogger.TraceExtension.XmlResponse.InnerXml;
I just could not figure out why I am getting error.
The Winforms app has an app.config
where I register my class library assembly to capture the xml. Here is my app.config
details
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="TestServiceSoap"/> </basicHttpBinding>
</bindings> <client> <endpoint address="http://localhost:6804/Service1.asmx"
binding="basicHttpBinding" bindingConfiguration="TestServiceSoap"
contract="ServiceRef.TestServiceSoap" name="TestServiceSoap"/>
</client>
</system.serviceModel>
<system.web>
<compilation debug="true" targetFramework="4.0" />
<webServices> <soapExtensionTypes>
<add type="SoapLogger.TraceExtension,SoapLogger" priority="1" group="0" />
</soapExtensionTypes> </webServices>
</system.web>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup> </configuration>
When I search google just to know how to capture request/response xml then I got this kind of many article. I followed many but nothing works.
This url I followed to get the job done http://jramirezdev.net/blog/c-tip-capturar-los-mensajes-soap-de-un-servicio-asmx-que-hemos-referenciado
What kind of mistake am I making not clear. I set the break point at every method in my class library method but when web service call no method got executed in the class library. I don't want to use any tool like wireshark, fiddler to capture the request/response xml rather want to do same thing programatically.
So please guide me what is my mistake? Why am I getting object reference not set error? Please have a look at my code or go to the url link and tell me what is wrong in my approach
Upvotes: 5
Views: 29042
Reputation: 2105
There is an another way to see XML SOAP - custom MessageEncoder. The main difference from IDispatchMessageInspector / IClientMessageInspector is that it works on lower level, so it captures original byte content including any malformed xml.
In order to implement tracing using this approach you need to wrap a standard textMessageEncoding with custom message encoder as new binding element and apply that custom binding to endpoint in your config.
Also you can see as example how I did it in my project - wrapping textMessageEncoding, logging encoder, custom binding element and config.
Upvotes: 0
Reputation: 211
Simply we can trace the request message as.
OperationContext context = OperationContext.Current;
if (context != null && context.RequestContext != null)
{
Message msg = context.RequestContext.RequestMessage;
string reqXML = msg.ToString();
}
Upvotes: 12
Reputation: 11601
The error was caused because xmlRequest/xmlResponse have not been initialized when you access them. Hence, try to check null, or create default instances for them.
From your code they are only initialized in the ProcessMessage(SoapMessage message){}
, make sure that the method is called before you call
var soapRequest = SoapLogger.TraceExtension.XmlRequest.InnerXml;
var soapResponse = SoapLogger.TraceExtension.XmlResponse.InnerXml;
Hope this helps.
EDIT:
After closely examining your code, I realized that the extension you wrote (SoapExtension) was for ASP.NET XML web services, and it had no business with a WCF service.
To inspect the request and reply in a WCF service, you can extend your dispatcher on the service side by implementing System.ServiceModel.Dispatcher.IDispatchMessageInspector.
You can find the example here http://msdn.microsoft.com/en-us/library/ms733104%28v=vs.100%29.aspx
Upvotes: 3
Reputation: 31780
Upvotes: 5