Saturday, March 22, 2008

Weakly typed webservices

Weakly typed programming versus strongly typed programming is one of the many religious wars fought by programmers. In my opinion it's all a matter of personal preference and I myself prefer strongly types languages. They give me the compile-time errors when I do something really stupid and I like that, even if it means I must work a little harder to convert one type to another.

On the other hand, I have found out that I don't like strongly typed webservices. What is a strongly typed webservice? Well, consider that a webservice is basically just some XML send over an HTTP connection (yes I know there are a lot more varieties, but when most people talk about webservices they tend to mean SOAP over HTTP), so everything is basically a string. A weakly typed webservice would have all its inputs and outputs defined as strings. A strongly typed webservice on the other hand would have its inputs and outputs defined as the type of data it's supposed to represent: ints, bools, DateTimes, etc.

To make the discussion a little more clear, here is a weakly typed webservice (in C#):

[WebMethod]
public string WeakWebservice(string inputDate);
And this is a stronly typed webservice:
[WebMethod]
public int StrongWebservice(DateTime inputDate);
So why would I want a weakly typed webservice?

Consider that all values are sent over HTTP as text, so that DateTime parameter 'inputDate' for the strongly typed webservice is possibly sent as "2008-03-20T18:39:12Z".

The SOAP stack at the webservice side will then take this string and interpret it so it can be turned into a DateTime object and given to the webservice entry point. At that point the code you or me wrote will get in on the action and is allowed to do stuff. Everything before that is done by the SOAP stack (in my case the ASP.NET SOAP stack).

Since everything is sent as text, there is no objection to calling the webservice using a client that doesn't understand the .NET DateTime class. All goes well as long as the client sends the text formatted in a way the SOAP stack expects it (as is defined in the SOAP specification). For as long as my client sends "2008-03-20T18:39:12Z", it will be interpreted and turned into a DateTime object for me. But what happens when I make an error in my string and I send something that can't be interpreted as a .NET DateTime object?

You'll get an error:

System.Web.Services.Protocols.SoapException: Server was unable to read request. ---> System.InvalidOperationException: There is an error in XML document (11, 74). ---> System.FormatException: Input string was not in a correct format. at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal) at System.Number.ParseInt64(String value, NumberStyles options, NumberFormatInfo numfmt) at System.Xml.XmlConvert.ToInt64(String s) at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReader1.Read3_CustomerInfo(Boolean isNullable, Boolean checkType) at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReader1.Read21_IptvGetAccountInfo() at Microsoft.Xml.Serialization.GeneratedAssembly.ArrayOfObjectSerializer.Deserialize(XmlSerializationReader reader) at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events) --- End of inner exception stack trace --- at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events) at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle) at System.Web.Services.Protocols.SoapServerProtocol.ReadParameters() --- End of inner exception stack trace --- at System.Web.Services.Protocols.SoapServerProtocol.ReadParameters() at System.Web.Services.Protocols.WebServiceHandler.CoreProcessRequest()

I don't know about you, but I find this error hardly useful. Now consider a webservice function with 3 objects as parameters, each object containing more parameters a few levels deep. How useful is this error then in finding which of the parameters is wrong? Granted this is specific for .NET and I have no idea how Java for example would handle this, but you see my point?

I my company we return error codes and error messages as part of the return value of the webservice. We call these "functional errors". We decide which error code means which error situation and we don't use SOAP Faults or HTTP error codes. That is, we don't want to. If the webservice isn't available, or the authentication goes wrong you'll still get an HTTP error and in the error shown above, you'll get a SOAP fault.

But if the specification says you need to send a string no more then 10 characters long, we check that in de code and return an error code if the string is to long. But if the specification says you need to send a number, but you send a word, then you'll get a SOAP Fault. That's not consistent.

The next time I'm tasked with creating a new webservice, I will make it weakly-typed. All the input and output will be defined as strings. That way I can validate the input myself and return a nice functional error when it's incorrect.

No comments: