Search code examples
c#asp.netweb-servicessoapasmx

Trap SOAP requests/response in C#


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


Solution

  • 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.