Monday, April 28, 2008

Uri.AbsoluteUri and Uri.ToString() are NOT the same

If you're familiar with the System.Uri class you know that it has a couple of ways of becoming a string for purposes of communication: its AbsoluteUri property and its ToString() method.  What I didn't find out until very recently was the significant difference between the two.  Since the difference led to functional bugs in production code, I thought I'd share the knowledge to help you avoid the same fate.

Given a Uri instance constructed like this:

UriBuilder builder = new UriBuilder("http://somehost/somepath");
builder.Query = "somekey=" + HttpUtility.UrlEncode("some+value");
Uri someUri = builder.Uri;

Uri.ToString() will return the human-readable URL: http://somehost/somepath?somekey=some+value

Notice the plus sign in "some+value", the way we put it originally, before HttpUtility.UrlEncode encoded it.

Uri.AbsoluteUri on the other hand will return the encoded form as HttpUtility.UrlEncode returned it: http://somehost/somepath?somekey=some%2bvalue

Notice this time how the plus sign has been (is still) encoded.

So why is this important?  Well if you make a request with some+value, then the web server will receive "some value".  But if you make a request with some%2bvalue, then the web server will receive "some+value", which is what you originally intended.  Plus signs aren't the only symbol that can be misinterpreted, of course. 

What lesson can we derive from this?  Unless you're displaying a URL for diagnostic purposes (and perhaps even then) always use Uri.AbsoluteUri instead of Uri.ToString().

Incidentally, UriBuilder.ToString() does not decode the URL, so UriBuilder.ToString() is safe to use.  It's just Uri.ToString() that you should avoid.

1 comment:

  1. I also noticed this recently. It's also important to note that UriBuilder also has its own quirks (like it accepts ?somekey=somevalue as a constructor parameter, but if you set the Query property to the same thing you'll end up with two ?'s). Strangely, for relative Uri's this brokenness does NOT occur, so it's safe to use ToString() for relative Uris (and that's one of the few methods you can even use on a Relative Uri).