DEV Community

Jonas Samuelsson
Jonas Samuelsson

Posted on

Constructing uris in dotnet is harder than it should

A common pattern for communicating over http in dotnet is to use a HttpClient with a base address and then add a relative path for every request. If the base address includes a parts of the path, one has to be very careful of where slashes are added or the resulting uri wont be what you expect.

We recently got bit by this at work where we suddenly started getting 404s on all requests going from one service to another.
As it turned out we had made a configuration change and a missing trailing slash in the base address was the reason.

This little sample demonstrates what happened

using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
  public static void Main()
  {
    var baseAddresses = new [] { "http://host.io/foo", "http://host.io/foo/" };
    var relativeAddresses = new [] { "bar", "/bar" };

    foreach (var baseAddress in baseAddresses)
    {
      foreach (var relativeAddress in relativeAddresses)
      {
        var uri = new Uri(new Uri(baseAddress), relativeAddress).AbsoluteUri;
        Console.WriteLine($"{baseAddress} + {relativeAddress} = {uri}");
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

And the output is

http://host.io/foo + bar = http://host.io/bar
http://host.io/foo + /bar = http://host.io/bar
http://host.io/foo/ + bar = http://host.io/foo/bar
http://host.io/foo/ + /bar = http://host.io/bar
Enter fullscreen mode Exit fullscreen mode

As you can see, in three out of four possible combinations the foo part of the base address is silently removed from the final uri. đŸ˜±

Top comments (2)

Collapse
 
paulasantamaria profile image
Paula SantamarĂ­a • Edited

I had the same issue a while ago. So annoying!
Every time I have to work with URIs on c# I wish the framework had something like System.IO.Path.Combine(params string[] paths) but for URIs.

Collapse
 
moejoe profile image
Moritz Haslhofer

i was annoyed too, until someone pointed out to me, that shockingly, this is the specified behavior by tools.ietf.org/html/rfc3986#sectio...

For the first two examples:

as far as i understand, the left url-part - if not ending in a / - could be a container or a file.

from an urls perspective there is no difference between host.io/foo and host.io/foo.html.

So the merging algorithm cuts everything on the left side, after the last slash.

For the 3rd and 4th example:

If the right url-part start with an "/" it is assumed that it is an absoulte path (starting with the resource-servers root).

So any path part after the root of the left side is cut.