DEV Community

Cover image for The Uri composition mystery
João Antunes
João Antunes

Posted on • Originally published at blog.codingmilitia.com on

The Uri composition mystery

Intro

This will be one of those posts of shame, that I'll use to make sure the next time I get into the same problem, I don't waste hours trying to figure it out 😛.

So, what's the problem?

So I was using an HttpClient, passing it an Uri for the request to make. To compose the Uri, the constructor that gets two Uris was being used, the first Uri represents the base address, so an absolute Uri, while the second should be a relative address.

With this in mind, looking at the following code, what would you expect to be written to the console?

var baseUri = new Uri("https://api.dev/v3");
var routeUri = new Uri("stuff", UriKind.Relative);
var fullUri = new Uri(baseUri, routeUri);

Console.WriteLine(fullUri);
Enter fullscreen mode Exit fullscreen mode

My expectation would be https://api.dev/v3/stuff, but alas, that's not what we get! The output is https://api.dev/stuff, because I didn't add a / to the end of the base address. If the base address ends with /, then the composition would work as expected.

But wait! There's more...

Even with the trailing slash in the base address, if the relative address starts with a slash, it will replace the relative part of the base address as well.

So, the following code:

var baseUri = new Uri("https://api.dev/v3/");
var routeUri = new Uri("/stuff", UriKind.Relative);
var fullUri = new Uri(baseUri, routeUri);

Console.WriteLine(fullUri);
Enter fullscreen mode Exit fullscreen mode

Will also output https://api.dev/stuff.

"Adding insult to injury"

What's worse than the time I wasted on this, is that this behavior is described in the docs.

Remarks

This constructor creates a new Uri instance by combining an absolute Uri instance, baseUri, with a relative Uri instance, relativeUri. If relativeUri is an absolute Uri instance (containing a scheme, host name, and optionally a port number), the Uri instance is created using only relativeUri.

If the baseUri has relative parts (like /api), then the relative part must be terminated with a slash, (like /api/), if the relative part of baseUri is to be preserved in the constructed Uri.

Additionally, if the relativeUri begins with a slash, then it will replace any relative part of the baseUri

This constructor does not ensure that the Uri refers to an accessible resource.

The behavior just seemed so strange to me (although there's probably a good reason for it), I didn't think about looking at the docs, and kept scouring the code for some other reason to what was happening.

Outro

Wrapping up, if we want to be sure the Uri composition works well, we should end the base address with a / and not start the relative part with one.

var baseUri = new Uri("https://api.dev/v3/");
var routeUri = new Uri("stuff", UriKind.Relative);
var fullUri = new Uri(baseUri, routeUri);

Console.WriteLine(fullUri);
// outputs -> https://api.dev/v3/stuff
Enter fullscreen mode Exit fullscreen mode

Hopefully I'll remember this the next time! 🙃

Oldest comments (0)