We use cookies to improve your experience on our website.

  • Home
  • Blog
  • Capturing (and using) raw SOAP messages in WCF

Capturing (and using) raw SOAP messages in WCF

WCF is great for building web services.  It's also great for interacting with existing web services.  Visual Studio makes it so easy . . . add a service reference, point to the WSDL of the service and just like that, you have a set of classes to handle the service and data contracts.
Unfortunately, sometimes web services don't live up to their contracts.  Recently, I was interacting with a web service and found that sometimes the response would be null.  I fired up Fiddler and looked at the SOAP messages.  Sure enough, an error had occurred on the remote server and the response, while valid XML, looked nothing like the promised Data Contract.  It did however provide a useful description of the error.
There was no SOAP fault . . . no exception thrown (unless I tried to use the null response without checking), just a null response object by the time WCF handed it to me.
I searched the web and found several suggestions.

  • Use Fiddler . . . tried that but it doesn't help when you are trying to get your service into production.
  • Build a SOAP Extension . . . that's great if you are building a legacy asp.net XML Web Service and just want to log or tweak the messages.  I'm doing WCF and needed to make the messages available to my service in real time.
  • Edit the Visual Studio Generated classes to wrap the response in XML and deserialize it yourself . . . that might work but you throw away so much niceness that's built for you already.
I ended up building a custom Endpoint behavior and applying it to the generated SOAP Client object.
 
First, you need a class to hold the raw Request and Response:
        public class InspectedSOAPMessages
        {
            public string Request { getset; }

            public string Response { getset; }
        }
  
 
Create an instance of the class and pass it into the new Endpoint Behavior:
 
        InspectedSOAPMessages soapMessages = new InspectedSOAPMessages();
        batchClient.Endpoint.Behaviors.Add(new CapturingEndpointBehavior(soapMessages));
        BatchService.batchResponseType response = batchClient.BatchOperation(batchRequest);
        batchClient.Close();
           //response will be null if the contract was violated . . . 
        if (response == null)
        {
           results.Message = soapMessages.Response;    } else { //process normal response
  
Now, all we need is the set of classes to handle the Endpoint Behavior:
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Description;
    using System.ServiceModel.Dispatcher;

    namespace MBSGuru
    {
    ///<summary>
        /// Allows capturing of raw SOAP Messages
        ///</summary>
        public class CapturingEndpointBehavior : IEndpointBehavior
        {
            ///<summary>
            /// Holds the messages
            ///</summary>
            public InspectedSOAPMessages SoapMessages { getset; }

            public CapturingEndpointBehavior(InspectedSOAPMessages soapMessages)
            {
                this.SoapMessages = soapMessages;
            }

            ///<summary>
            /// Required by IEndpointBehavior
            ///</summary>
            ///<param name="endpoint"></param>
            ///<param name="bindingParameters"></param>
            public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { return; }

            ///<summary>
            /// Required by IEndpointBehavior
            ///</summary>
            ///<param name="endpoint"></param>
            ///<param name="clientRuntime"></param>
            public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
            {
                clientRuntime.MessageInspectors.Add(new CapturingMessageInspector(this.SoapMessages));
            }

            ///<summary>
            /// Required by IEndpointBehavior
            ///</summary>
            ///<param name="endpoint"></param>
            ///<param name="endpointDispatcher"></param>
            public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { return; }

            ///<summary>
            /// Required by IEndpointBehavior
            ///</summary>
            ///<param name="endpoint"></param>
            public void Validate(ServiceEndpoint endpoint) { return; }
        }

        ///<summary>
        /// Actual inspector that captures the messages
        ///</summary>
        public class CapturingMessageInspector : IClientMessageInspector
        {
            ///<summary>
            /// Holds the messages
            ///</summary>
            public InspectedSOAPMessages SoapMessages { getset; }

            public CapturingMessageInspector(InspectedSOAPMessages soapMessages)
            {
                this.SoapMessages = soapMessages;
            }

            ///<summary>
            /// Called after the web service call completes.  Allows capturing of raw response.
            ///</summary>
            ///<param name="reply"></param>
            ///<param name="correlationState"></param>
            public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
            {
                this.SoapMessages.Response = reply.ToString();
            }

            ///<summary>
            /// Called before the web service is invoked.  Allows capturing of raw request.
            ///</summary>
            ///<param name="request"></param>
            ///<param name="channel"></param>
            ///<returns></returns>
            public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel)
            {
                this.SoapMessages.Request = request.ToString();
                return null;
            }
        }
    }
  
And just like that, you have a copy of the request and response.  How you handle the violation of the contract is up to you.  At least now you can report something more informative than "Error Occurred"
 
This blog has been relocated from http://mbsguru.blogspot.com/ with authorization.
Lance Russell

VP, System Design/Architecture

Looking for spaghetti code that is nearly impossible to maintain and upgrade?  Then stay away from Lance.  Far away.   Lance’s highly-logical mind can only create organized, well-written applications and integrations that are a breeze to maintain.   There is no business application coding problem that Lance cannot figure out.  One of our hi-profile, global clients tried to make him fail.  He did not and they came back with the quote “Lance has to be one of the best SharePoint consultants in the world”.   Those are definitely true words.   His expertise in all areas of software development, including Dynamics CRM and ERP, Integrations and web sites is top-notch.  And he is funny too.  You’ll appreciate his humorous approach when you see everyone gelling, working together happily, and your project humming along smoothly.

Recent Projects

In a recent project, Lance worked with a global manufacturer of gaming equipment to restructure their entire technology platform. He created Item Master, a SharePoint web-based application used to manage the approval process of changing inventory items, reducing approvals from 2+ weeks to less than 2 days. He also created a product configurator, which automated the time consuming and cumbersome pick/pack/ship process of their orders which were typically customized and highly complicated. To top it all off, he tied it all together with a much needed compliance component that checked orders against their jurisdiction’s regulations.

the most surprising part of working with them was how well they worked with our team. Their calm and professional demeanour throughout the project put people at ease and everyone clicked from day one. The relationship was truly unique, in that you just don’t see that level of trust, bonding and collaboration.”

1000 Characters left