.NET WebRequest Content-Type Gotcha

Posted by Tom on 2014-12-11 20:45

Here's a fun one.

We're making a call to a third-party web service which was failing with a 401 and blaming our HMAC. But only on POSTs. Fair enough - it's probably encoding. It's always encoding. So I downloaded their Python library and diffed the signatures with the same input and they matched. So what's the problem? Here's the code.

WebRequest request = WebRequest.Create(url);

string body = WebUtils.BuildQuerystring(dataProvided);
byte[] byteArray = Encoding.UTF8.GetBytes(body);

using (Stream dataStream = request.GetRequestStream())
{
    dataStream.Write(byteArray, 0, byteArray.Length);
}

request.ContentLength = byteArray.Length;
request.ContentType = "application/x-www-form-urlencoded";

Spot the problem? Me either. After snagging the requests generated by their official Python SDK and comparing them with the request created by my code I noticed something odd. In my request the Content-Type header is missing. Yes, that one I set up there on line 11. Which is odd.

Turns out this line here:

WebRequest request = WebRequest.Create(url);

should be:

WebRequest request = HttpWebRequest.Create(url);

because otherwise everything else works, but the ContentType property does literally nothing. Not in a useful way like throwing a NotImplementedException. It just takes your Content-Type, throws it in /dev/null and then gives you a thumbs up and a cheesy grin. When it comes to sending the request to the API it simply misses the Content-Type header completely. So it kind of was an encoding issue after all.

Oh, as another aside, once you've switched from a WebRequest to an HttpWebRequest you also need to set your ContentLength and ContentType properties before you start writing to the request stream, because . . . I don't know. Reasons? It's usually reasons.

Honestly - what would I do without Fiddler?