Please drop the SOAP

By Adam Coffman.

My node.js project is coming along well and I am in the process of writing up a couple of detailed blog posts about it, but in the meantime I’ll leave you with this rant about SOAP.

While at work today I was writing some code to interface with Microsoft’s AdCenter API which, if you didn’t know, utilizes SOAP. My end goal was to POST some data to AdCenter, I had approximately 100 entries, each consisting of three primitive data fields (two 64 bit ints and a string). With any modern, usable, API this would be a trivial task completable in 15 minutes or less - but oh no. Not with SOAP.

For starters you have to point Visual Studio (sigh) to the service reference, at which point nine thousand (yes, that’s nine-zero-zero-zero) lines of code are generated. Next you have to write code to construct your objects and initialize your SOAP client. Only then are you able to actually use the API.

Anyways, all of this went more or less according to plan until I actually tried to run the code. I got an error. Not a descriptive error, or an HTTP status code, or anything that could be used to track down the problem but a “SOAP Error.” That was it. The whole text of the error from the Microsoft API was “SOAP Error.” Well, “no problem” I thought - surely I could step into the method I was calling using my debugger and see what was happening. Unfortunately I was wrong. All I really wanted was the ability to see the XML envelopes that were being generated so I could find out what was happening but alas, Visual Studio abstracts this away from you entirely. I asked a co worker and he helpfully pointed me to some web resources on how to inspect SOAP messages. The absurdity of the solution blew my mind. Keep in mind all I wanted to do is SEE what messages are being passed to the API - a simple enough request.

Long story short, in order to inspect SOAP messages you have to write a class that implements the ICLientMessageInspector interface. This class must implement the AfterReceiveReply and BeforeSendRequest methods. As you’ll see in my example code, I simply output the message to the console and write it to a file so I can look at it later.

public class ConsoleOutputMessageInspector 
: IClientMessageInspector
{
  public void AfterReceiveReply
      (ref Message reply, object correlationState)
  {
      var fileWriter = new StreamWriter("C:/reply.txt");
      fileWriter.WriteLine(reply);
      fileWriter.Flush();
      fileWriter.Close();
      Console.WriteLine("Received:\n{0}", reply);
  }

  public object BeforeSendRequest
     (ref Message request, IClientChannel channel)
  {
      var fileWriter = new StreamWriter("C:/request.txt");
      fileWriter.WriteLine(request);
      fileWriter.Flush();
      fileWriter.Close();
      Console.WriteLine("Received:\n{0}", request);
      return null;
  }
}

Okay, simple enough, but how do we hook it up? Well, for that we need to create another class. This one must implement the IEndpointBehavior interface. There are four methods we must implement in order to use this interface - but we only care about one of them: ApplyClientBehavior. We need to add our newly created message inspector to the list of client message inspectors.

public class ConsoleHeaderOutputBehavior 
: IEndpointBehavior
{

  public void Validate(ServiceEndpoint endpoint)
  {
  }

  public void AddBindingParameters
  (ServiceEndpoint endpoint, BindingParameterCollection param)
  {
  }

  public void ApplyDispatchBehavior
  (ServiceEndpoint endpoint, EndpointDispatcher dispatcher)
  {
  }

  public void ApplyClientBehavior
    (ServiceEndpoint endpoint, ClientRuntime clientRuntime)
  {
      var inspector = new ConsoleOutputMessageInspector();
      clientRuntime.MessageInspectors.Add(inspector);
  }
}

Alright, now we’ve done that - but our code still doesn’t actually do anything. What we need to do next is enable our newly created behavior. You can do this one of two ways - in your application configuration or programmatically. I chose the latter. To do this you add your new behavior to your instance of the SOAP client like so:

var behavior = new ConsoleHeaderOutputBehavior();
_client.Endpoint.Behaviors.Add(behavior); 

Now, finally, the SOAP XML envelopes that our client sends and receives will be written to a file and to the output window for inspection. As you can see, the code is dead simple, but its incredibly frustrating to be forced to jump through hoops in order to do something as basic as seeing what messages your application is sending and receiving. As a proponent of RESTful interfaces its even more frustrating because I’m used to simply typing curl "resourcename" > file to accomplish the exact same functionality.

In my mind SOAP’s biggest failure is what some people consider to be its biggest success - it abstracts away all notion of the underlying HTTP model and replaces it with crutches like code generation.

As an aside - I later ditched the code I’ve shown here and just went with Wireshark. I was able to capture and reassemble the XML messages with it, and it provided a much nicer interface for searching and sorting them.