HTTP 400 Response from .Net Compact Framework WebServices

I recently encountered a problem consuming a web service from the compact framework. I am targeting Windows CE 5.0 on a Motorola WT4090 device and consuming a standard .Net Web Service via an application written in VB.Net version 3.5

The web service function signature in question was similar to the following;

<WebMethod()> _
Public Function GetArticleNumber(ByVal Barcode As String) As Integer

(the internal code of this function is not significant - it simply looks up a barcode against a database and returns an identifying number)

When scanning particular barcodes, the web service returned a HTTP 400 (Bad Request) response.

There were no entries in the HTTPERR.LOG file generated by IIS - this indicates the bad request is not generated by IIS - the HTTP request sent to the server is perfectly valid. The 400 status code must be rendered by something processing the request. Any application (such as ASP.Net) can manipulate the status codes of a HTTP response.

Debugging a request to the web service resulted in none of my code ever being executed. Strange. Something between my application and IIS is parsing the request and rejecting it.

After a few hours of deliberating, I finally bit the bullet and installed Microsoft's Network Monitor on the IIS server to view the HTTP payloads exchanged between the device and the server. Nothing looks out of the ordinary.

There is only one difference between the request that works and the request that fails. The payload that fails contains an ASCII character:
&#x1D;

Initially, I dismissed this. There is nothing wrong with that - it's correctly encoded, so why should it cause my request to fail? Well, it turns out it's not valid. For reasons I won't even pretend to understand, characters lower than 0x20 are not valid in XML 1.0. I have many issues with this;

Why does the .Net framework encode these characters for transmission without any exceptions raised? If it's not valid it should not allow me to call my web service with these characters. Why waste time and network bandwidth on a request we could easily have spotted would fail?

Why are these characters invalid? They are properly encoded! I suspect this is a limitation of using XML 1.0 rather than 1.1 but still - if I encode a character literal there is no need to validate it at all - it's encoded - it's not like it could be used in the transfer protocol in any way!

Anyway, issues aside I needed a fix. Luckily, there's a great article about this very issue but it's in reverse - it handles responses from the web service to the client. The other problem is that the original data is lost - it simply replaces these characters with spaces.

Luckily, in my case, a quick and dirty fix is acceptable. I know that functions that accept barcodes are the only functions likely to come up against this issue. As such, I'm happy to "prepare" and "decode" the barcode on each side of the transmission.

I figured I'd use at least some form of standard, so opted for the URLEncode method and URLDecode methods on either side. This will send the character as %1D and a call to URLDecode inside the web service method will decode this back to the original character. Only problem is, there's no URLEncode on the compact framework (at least, not without adding yet more references to my project.)

I decided to create a simple function to do the dirty job of URLEncoding these characters;

Public Shared Function PrepareString(ByVal input As String) As String
Dim outStr As String
'% = dec 37, hex 25
outStr = input.Replace("%"c, "%25")
For i As Integer = 0 To 31
outStr = outStr.Replace(Chr(i), "%" & i.ToString("X").PadLeft(2, "0"c))
Next
Return outStr
End Function

This function, in conjunction with the URLDecode in the web method "GetArticleNumber" meant I was now able to pass these characters between the device and the web server. Not an ideal solution and one, in my opinion, that shouldn't have been necessary in the first place.

Comments