November 11, 2013

Trace SOAP Request/Response XML with TraceExtension - SoapExtension - C#

Trace SOAP Request/Response XML with TraceExtension - SoapExtension - C#


In one of my projects, I had to interact with a Web Service (Java) from another NET Web Service (asmx). logs to leave messages back and forth, I had to resort to SoapExtension.
Sometimes we need to send a sample of our request SOAP / XML web service response. I leave you a practical example without much return, I hope will be helpful

Class TraceExtension

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Services.Protocols;
using System.IO;
using System.Xml;

namespace TestProject
{
    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
    }
}
Method soapMessageRecord

private void soapMessageRecord(string nombreMetodo)
        {
            StreamWriter escritorReq = File.CreateText("C:\\logs\\request_" + nombreMetodo+ "_" + DateTime.Now.Ticks + ".xml");
            StreamWriter escritorRes = File.CreateText("C:\\logs\\response_" + nombreMetodo+ "_" + DateTime.Now.Ticks + ".xml");


            // Object is accessed "XMLRequest" TraceExtension class and call property "OuterXml".
            string soapRequest = TraceExtension.XmlRequest.OuterXml;
            string soapResponse = TraceExtension.XmlResponse.OuterXml;

            escritorReq.Write(soapRequest);
            escritorReq.Close();
            //Access to the object "XmlResponse" TraceExtension class and called his property "OuterXml".
            escritorRes.Write(soapResponse);
            escritorRes.Close();
        }

Mode of use; Example

private void getStudentData(int identification)
        {
            PJWS.proxy2013 ws = new PJWS.proxy2013();
            PJWS.getStudentDataResponse data = new PJWS.getStudentDataResponse();
            data = ws.getData(identification);

            //call the above method most suitable
            soapMessageRecord("getStudentData");

            // or simply gain access in this way.
            var soapRequest = TraceExtension.XmlRequest.OuterXml;

            // or simply gain access in this way
            var soapResponse = TraceExtension.XmlResponse.OuterXml;
        }

In the web.config must add the following lines:

    <system.web>
      <compilation debug="true" targetFramework="4.0" />
      <webServices>
        <soapExtensionTypes>
          <add type="TestProject.TraceExtension, TestProject" priority="1" group="0" />
        </soapExtensionTypes>
      </webServices>
    </system.web>
See Also:

Ditulis Oleh : Angelo Hari: 3:15 PM Kategori:

21 comentarios:

  1. Doesn't work for me where you put:

    var soapRequest = TraceExtension.XmlRequest.OuterXml;

    "XmlRequest" is null. I can't recover the soap message.

    The only way it works for me is writing in a file, but I need another solution. I can't use files in my app.

    ReplyDelete
    Replies
    1. Have you made ​​sure that the request is not null?
      This method works when you go to consume WS from a client.

      Delete
    2. Could you send me your code snippet, admin@systemdeveloper.info

      Delete
    3. Angelo, so you just copy and paste this crappy code from another punk like you. Nice! Way to go.

      Delete
  2. Thanks for answer! Fortunelly I made it work jeje. I forget to register the soapextension class in web.config file. But I had to modify the TraceExtension class because the request where mixed. That is, the request A mingled with the response B.

    ReplyDelete
    Replies
    1. Great, it would be interesting if you could share your modified code .. xD

      Delete
    2. Sure! Here it is:

      First I added a new field:
      private int session;

      Second, modified the Initialize method:
      public override void Initialize(object initializer)
      {
      if (session == 0)
      session = System.Threading.Thread.CurrentThread.GetHashCode();
      }

      Finally, modified the AfterSerialize and BeforeDeserialize stages:
      case SoapMessageStage.AfterSerialize:
      if (session == System.Threading.Thread.CurrentThread.GetHashCode())
      {
      xmlRequest = GetSoapEnvelope(newStream);
      CopyStream(newStream, oldStream);
      }
      break;
      case SoapMessageStage.BeforeDeserialize:
      if (session == System.Threading.Thread.CurrentThread.GetHashCode())
      {
      CopyStream(oldStream, newStream);
      xmlResponse = GetSoapEnvelope(newStream);
      session = 0; //restart session variable
      }
      break;

      Comparing the hash of the current thread I could avoid to mix several request and responses. Thanks again for iluminate me!

      Delete
    3. Glad you has been useful!!!

      Delete
    4. Sorry i'm a beginner.

      I create form application as a web service client.

      where i use this code ???










      in my client or in web service?

      Delete
  3. Hi Angelo. I have the same problem as (AnonymousApril 2, 2014 at 3:18 AM).
    "XmlRequest" is null.
    I call "soapMessageRecord" exactly after invoking web service method, but for some reason XmlReques comes as null
    Also I tried to use TraceExtension as DLL building it in another project, as well as using it as class within the project where I call web service. In both cases XMLRequest is null

    ReplyDelete
  4. This comment has been removed by a blog administrator.

    ReplyDelete
  5. This comment has been removed by a blog administrator.

    ReplyDelete
  6. This comment has been removed by a blog administrator.

    ReplyDelete
  7. This comment has been removed by the author.

    ReplyDelete
  8. getStudentData is a webmethod or what , pls explain

    ReplyDelete
    Replies
    1. It was my mistake, now it is already fixed. Thanks

      Delete
  9. in my case i am not able to add soapExtensionTypes to web.config. it giving metadata error .







    ReplyDelete
  10. This comment has been removed by the author.

    ReplyDelete
  11. This comment has been removed by the author.

    ReplyDelete
  12. " Failed to add a service. Service metadata may not be accessible. Make sure your service is running and exposing metadata. " error after adding soapExtensionTypes to web.config

    ReplyDelete