[January 2, 2012 Update: A number of changes have been made to the NDFD web service since I originally posted this article. Both the URL and the methods exposed by the service have changed. The text below and downloads have been updated to reflect these changes.]
I like to know what the weather is going to be – it sets the tone for the day. But it is getting more difficult to get access to weather forecasts from the local television stations. Clearly lots of folks must tune in just for the weather because there seem to be this cat-and-mouse game where they parcel out snippets of info with “My complete forecast” always happening some time later…
National Weather Service to the rescue. They have a nice web site where you can get your local 7-day forecast (updated hourly). But what’s even more interesting, if you click around on the map you can get a forecast for anywhere in the US, even for places that aren’t even close to a NWS field office (this keeps getting better and better) and all this data is available from a SOAP-based web service, the National Digital Forecast Database (NDFD). So it looks like there is a way to get weather forecasts– and I don’t have to wait for the “News at 5”. And I can stick them in a desktop Gadget, in a Word document or on a PowerPoint slide.
The NWS provides a WSDL document so you might think it would be a simple thing to add a Service Reference to your project, have a proxy automatically generated, and you’re ready to roll. But unfortunately there is a wrinkle. The app.config file that’s auto generated from the NDFD WSDL document specifies that we’re going to use the stock basicHttpBinding and every time you send a SOAP request to the service you see this:
System.ServiceModel.ProtocolException was unhandled. Message=”The content type text/xml; charset=ISO-8859-1 of the response message does not match the content type of the binding (text/xml; charset=utf-8). If using a custom encoder, be sure that the IsContentTypeSupported method is implemented properly.
In this case, the problem really doesn’t have anything to do with correctly implementing IsContentTypeSupported. It’s caused by the fact that the NDFD service uses ISO-8859-1 encoding. However the MessageEncoder used by the basicHttpBinding only understands UTF-8, UTF-16 and Big-Endian Unicode… So finally there’s an excuse to create a custom binding. And the good news is that although this might be tedious, it’s not hard and there is a Microsoft sample that does exactly what we need. Instead of using the “optimized” XML reader and writer used by WCF, the sample uses a plain ole System.Xml.XmlReader and System.Xml.XmlWriter which do support ISO-8859-1. So aside from having to add a custom MessageEncoder, MessageEncoderFactory, MessageEncodingBindingElement and BindingElementExtensionElement to our project (just cut and paste from the Microsoft sample), things really aren’t too bad.
Once the custom MessageEncoder (and friends) are in place we still need to modify the auto-generated system.ServiceModel section of our app.config file (actually, in this case we’re going to just replace the whole section). Here is what it looks like:
<system.serviceModel> <bindings> <customBinding> <binding name="DefaultBinding"> <StmEncoding messageVersion="Soap11" encoding="ISO-8859-1" /> <httpTransport /> </binding> </customBinding> </bindings> <client> <endpoint address="http://graphical.weather.gov/xml/SOAP_server/ndfdXMLserver.php" binding="customBinding" bindingConfiguration="DefaultBinding" contract="NdfdServiceReference.ndfdXMLPortType" name="ndfdXMLPort" /> </client> <extensions> <bindingElementExtensions> <add name="StmEncoding" type="StmEncoderLibrary.StmEncoderBindingElementExtensionElement, StmEncoderLibrary"/> </bindingElementExtensions> </extensions> </system.serviceModel>
The first thing required is a new endpoint.
<endpoint address="http://graphical.weather.gov/xml/SOAP_server/ndfdXMLserver.php" binding="customBinding" bindingConfiguration="DefaultBinding" contract="NdfdServiceReference.ndfdXMLPortType" name="ndfdXMLPort" />
The address and contract are the same ones that were auto-generated from the NDFD WSDL document, but instead of using a basicHttpBinding, we specify a customBinding which gives us complete control of the WCF messaging stack. The specific custom binding we’re going to use in this case is named “NdfdBinding”. The name of the endpoint is how we are going to reference this endpoint when we create the service proxy in our application.
The next piece is the customBinding.
<customBinding> <binding name="DefaultBinding"> <StmEncoding messageVersion="Soap11" encoding="ISO-8859-1" /> <httpTransport /> </binding> </customBinding>
You probably don’t recognize the StmEncoding element (neither does Visual Studio IntelliSense). It’s a custom element that references a binding extension – this is how our custom MessageEncoder implementation gets into the act. Note that StmEncoding has messageVersion and encoding attributes – this is where we specify now our encoder is going to work – they are the values that eventually get passed to our custom MessageEncoderFactory and then to our custom MessageEncoder constructor.
Finally the bindingExtension:
<bindingElementExtensions> <add name="StmEncoding" type="StmEncoderLibrary.StmEncoderBindingElementExtensionElement, StmEncoderLibrary"/> </bindingElementExtensions>
This is where we specify the fully-qualified name of our class derived from BindingElementExtensionElement and the assembly containing the entire custom message encoder implementation.
Well, that’s it. Once you have the custom MessageEncoder that handles ISO-8859-1 and an appropriately constructed app.config file you should be able to talk to the NDFD web service and get a weather forecast whenever you want.
Next I think I’ll make the XML data a bit more readable.