Inspired by Mike Taulty’s A slightly different ‘hello, world’ for Indigo blog entry, I decided to create a low-level Indigo client that talks to flickr.
I wanted to understand how the Indigo plumbing worked, so I decided to forego the high-level APIs and write my code using nothing more than ChannelFactory, Message, IRequestChannel and friends. This code compiles and runs on the November CTP bits, so my code looks a bit different than Mike’s code on his blog. If you want to follow along, you’ll need the November CTP bits and the SDK, but be warned: the documentation has not stayed in sync with changes to the API. Reflector is your friend :)
It all started innocently enough. Here’s the original version of my code:
ChannelFactory<IRequestChannel> factory;
using (factory = new ChannelFactory<IRequestChannel>(
CreateBinding(),
new EndpointAddress("http://www.flickr.com/services/soap/"))) {
using (IRequestChannel channel = factory.CreateChannel()) {
StringReader body =
new StringReader(@"<x:FlickrRequest xmlns:x='urn:flickr'>
<api_key>83293ff34e3ab3d9935018480190319f</api_key>
<format>soap2</format>
<method>flickr.test.echo</method>
<name>value</name>
</x:FlickrRequest>");
XmlTextReader reader = new XmlTextReader(body);
Message message = Message.CreateMessage(MessageVersion.Soap12Addressing1,
"*", reader);
Message response = channel.Request(message);
XmlDictionaryReader responseReader = response.GetReaderAtBodyContents();
Console.Write(responseReader.ReadOuterXml());
}
}
This code uses a ChannelFactory to create a request/response style channel to flickr. Next, I construct a Message object and inject some XML into the message body. The flickr API is really just a single method that takes an XML document and returns an XML document. See the documentation for more examples. Finally, I send my request Message via the channel, and it hands me back a response Message object that I serialize to the console. Simple enough, right?
That’s when the fun began. There’s a bug in flickr, and apparently, I’m not the first one to report this. The flickr SOAP API returns a content-type of text/xml for a SOAP 1.2 message body, whereas the spec clearly states that the content-type should be application/soap+xml. Indigo is not happy at all with this, and throws a ProtocolException and aborts further processing of the HTTP response.
The crux of the problem lies in the type of channel that I created using the ChannelFactory class. To understand how the channel is constructed, I have to show you the missing implementation of the CreateBinding method:
CustomBinding binding = new CustomBinding();
binding.Elements.Add(new TextMessageEncodingBindingElement(
MessageVersion.Soap12Addressing1, Encoding.UTF8));
binding.Elements.Add(new HttpTransportBindingElement());
return binding;
Here, I tell the ChannelFactory to construct a channel that uses an HTTP transport, and a text-based XML serializer that will generate a SOAP 1.2 message that is text encoded as UTF-8. Indigo will also check to make sure that the response from the server is a SOAP 1.2 message and the content-type is application/soap+xml. To see this checking in action, try sending a SOAP 1.1 Message using this channel.
I spent some time spelunking around using Reflector, TextMessageEncoder.IsContentTypeSupported is where the ProtocolException gets thrown. Unfortunately, I couldn’t find a way to change the ContentType property of this object. This left only one other option that I could see: write a custom MessageEncoder.
More on how to do this tomorrow.
Recent Comments