I am trying to trap the request/response xml of my soap communication. I would love to have used WCF, but must comply with the employer request. So, I have this sample asmx website with this webmethod in my service:
[WebMethod]
public int Multiply(int x, int y)
{
return x * y;
}
The site is added to IIS and I can see it in browser:
http://localhost/WebService/Service.asmx?op=Multiply
and it works when I give it two int values.
I am trying to trap the SOAP body request that is getting sent and received. According to my research, if I add a soap extension, I would be able to do that. So, I am looking at this example: https://stackoverflow.com/a/10613433/1019042 and trying to imitate it.
I added a class library project to my solution with this class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Services.Protocols;
using System.IO;
using System.Reflection;
namespace SoapExtensions
{
public class SoapLoggerExtension : SoapExtension
{
//private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private Stream oldStream;
private Stream newStream;
public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
{
WriteMessages("inside the getinitializer1");
return null;
}
public override object GetInitializer(Type serviceType)
{
WriteMessages("inside the getinitializer2");
WriteMessages("inside the getinitializer");
return null;
}
public override void Initialize(object initializer)
{
WriteMessages("inside the Initialize");
}
public override System.IO.Stream ChainStream(System.IO.Stream stream)
{
WriteMessages("inside the ChainStream");
oldStream = stream;
newStream = new MemoryStream();
return newStream;
}
public override void ProcessMessage(SoapMessage message)
{
WriteMessages("inside the ProcessMessage");
switch (message.Stage)
{
case SoapMessageStage.BeforeSerialize:
break;
case SoapMessageStage.AfterSerialize:
Log(message, "AfterSerialize");
CopyStream(newStream, oldStream);
newStream.Position = 0;
break;
case SoapMessageStage.BeforeDeserialize:
CopyStream(oldStream, newStream);
Log(message, "BeforeDeserialize");
break;
case SoapMessageStage.AfterDeserialize:
break;
}
}
private void WriteMessages(string message)
{
StreamWriter sw = new StreamWriter(@"C:\MyFolder\LogFolder\myLog.txt", true);
sw.WriteLine(message);
sw.Close();
}
public void Log(SoapMessage message, string stage)
{
newStream.Position = 0;
string contents = (message is SoapServerMessage) ? "SoapRequest " : "SoapResponse ";
contents += stage + ";";
StreamReader reader = new StreamReader(newStream);
contents += reader.ReadToEnd();
newStream.Position = 0;
WriteMessages(contents.ToString());
//log.Debug(contents);
}
void ReturnStream()
{
WriteMessages("inside the ReturnStream");
CopyAndReverse(newStream, oldStream);
}
void ReceiveStream()
{
WriteMessages("inside the ReceiveStream");
CopyAndReverse(newStream, oldStream);
}
public void ReverseIncomingStream()
{
WriteMessages("inside the ReverseIncomingStream");
ReverseStream(newStream);
}
public void ReverseOutgoingStream()
{
WriteMessages("inside the ReverseOutgoingStream");
ReverseStream(newStream);
}
public void ReverseStream(Stream stream)
{
WriteMessages("inside the ReverseStream");
TextReader tr = new StreamReader(stream);
string str = tr.ReadToEnd();
char[] data = str.ToCharArray();
Array.Reverse(data);
string strReversed = new string(data);
TextWriter tw = new StreamWriter(stream);
stream.Position = 0;
tw.Write(strReversed);
tw.Flush();
}
void CopyAndReverse(Stream from, Stream to)
{
WriteMessages("inside the CopyAndReverse");
TextReader tr = new StreamReader(from);
TextWriter tw = new StreamWriter(to);
string str = tr.ReadToEnd();
char[] data = str.ToCharArray();
Array.Reverse(data);
string strReversed = new string(data);
tw.Write(strReversed);
tw.Flush();
}
private void CopyStream(Stream fromStream, Stream toStream)
{
WriteMessages("inside the CopyStream");
try
{
StreamReader sr = new StreamReader(fromStream);
StreamWriter sw = new StreamWriter(toStream);
sw.WriteLine(sr.ReadToEnd());
sw.Flush();
}
catch (Exception ex)
{
string message = String.Format("CopyStream failed because: {0}", ex.Message);
WriteMessages("Message is: " + message);
WriteMessages("ex is: " + ex);
// log.Error(message, ex);
}
}
}
[AttributeUsage(AttributeTargets.Method)]
public class SoapLoggerExtensionAttribute : SoapExtensionAttribute
{
private int priority = 1;
public override int Priority
{
get { return priority; }
set { priority = value; }
}
public override System.Type ExtensionType
{
get { return typeof(SoapLoggerExtension); }
}
}
}
I added a message to every method just to see if anything gets called in my extension. and then went to my service and added a reference to the project. I went to the web.config of the service and added this under system.web:
<webServices>
<soapExtensionTypes>
<add type="SoapExtensions.SoapLoggerExtension, SoapExtension"
priority="1" group="0" />
</soapExtensionTypes>
</webServices>
The problem is that I don't get anything written in the mylog.txt like it is not hitting my soapservice. The file resides in a folder that Everyone has full access to it. Shouldn't the call to the Multiply log something via the soapextension? Are my settings wrong? Is there something else I should be doing? I am using framework 3.5
try with
<webServices>
<soapExtensionTypes>
<add type="SoapExtensions.SoapLoggerExtension, SoapExtensions"
priority="1" group="0" />
</soapExtensionTypes>
</webServices>
you're missing the 's' in the assembly name for the type.
Also, once you start up the browser at http://localhost/WebService/Service.asmx?op=Multiply
, use Visual Studio and attach to the process.
This way, you can debug the incoming multiple call and trace through the flow. you'll see if the Extension point was hit.