The .NET Uri class is powerful in a number of scenarios, but can be unweildy at times. I've created a set of extensions to the Uri class (and tested them thoroughly) to make a range of interactions with the Uri class downright fun. These improvements include

The Uri class in .NET, like similar classes in other frameworks, does a lot of heavy lifting.

If you've ever spent time digging into the specifications on URIs and the many different behaviors for different schemes then you know you don't want to spend any more time on this than necessary.

I decided to encapsulate a number of helpful methods from my own code libraries into a well-tested, robust package for public consumption. What I thought would take a couple days became a much longer task, but the result should save others and my future self a lot of time and headache.

Use of Uri Can Be Tedious

One of the biggest annoyances when using the Uri class is dealing with the various delimiting characters.

http://www.domain.com/path1/path2?queryitem=value&q2=value2#fragment

In a typical URL, a colon follows the scheme (http:), slashes delimit the scheme from the authority (//), a question mark (?) begins the query string and hash (#) denotes the fragment. And more...

The Uri class is a bit inconsistent regarding delimiters.

For example, the Uri.Query property contains the leading question mark:

?queryitem=value&q2=value,

but the scheme does not include the trailing colon

http.

Nor does the authority include the leading double slashes

www.domain.com

but the path does include a leading slash

/path1/path2,

and the fragment includes the leading hash:

#fragment.

I'm not saying the Uri class is broken, it's not, I've tested it a lot and it's pretty solid...just annoying.

There are ways on the Uri class to get these components with and without delimiters, but those are a bit cumbersome and inconsistent, too.

Because of these inconsistencies, an overarching goal of the Cambia.UriExtensions package is to make futzing with delimiters a thing of the past.

All you care about is the data in the URI. You'd rather just trust the class to parse and join the parts correctly.

I won't go into further detail on Uri quirks. It's beyond the scope of this article. Just take my word for it. I've spent the last week testing all the nuances of Uri and UriBuilder while sifting through RFC3986, the definitive spec on URIs.

Most of the Uri quirks will never affect you, but if you want a robust Uri extensions package, well, someone has to think about this stuff. (Lucky me.)

The various Uri properties appear inconsistent when it comes to including or excluding delimiting characters. I'm not saying it's wrong, just annoying.

As I said, when returned from the GetPart method, none of these parts will have leading or trailing delimiters. That even includes the Path. It will not begin with a slash.

The .NET framework also contains an enumeration of Uri parts, but it's called UriComponents. There are a couple of parts in my UriPart enumeration that are not present in UriComponents.

Specifically, AuthorityWithUserInfo and SchemeAndAuthorityWithUserInfo are new concepts.

According to RFC3986, the Authority actually contains three sections: user info, host and port. However, Uri.Authority only contains the host and port.

Consider the following URI.

ftp://steve@host.com:555/path

The above ftp address is valid and you can see that the host is preceded by steve@. That's user information. The Authority should contain that portion, but you'll find in the Uri.Authority property that it does not. This is why I have added UriPart.AuthorityWithUserInfo to represent a proper definition of Authority.

Meanwhile, I've maintained the Authority part as containing only host and port in order to have naming consistency with the Uri class.

Other than these differences, GetPart has similar capabilities as Uri.GetComponents. Use either as it suits your purpose.

GetPart Examples

Notice that none of the results in the following calls to GetPart have leading or trailing delimiters, thus yielding a more consistent and predictable result.

ToUrlType Method Intro

As with the GetPart method, the ToUrlType method takes an enum argument. In this case, it's fittingly called UrlType.

using Cambia;
public enum UrlType
{
None,
// Starts with a scheme and is a full, complete URI including everything that follows.
AbsoluteUri,
// Starts with a single slash. Includes the path and everything that follows.
RootRelative,
// Starts with two slashes. Includes everything except the scheme and its trailing colon.
SchemeRelative,
// Starts with a tilde. Relative to the application root. Only available in an
// ASP.NET context.
AppRelative
}

ToUrlType Examples

Let's look at some examples.

using Cambia;
Uri u = new Uri("http://www.domain.com/folder/app/file.aspx?a=hello");
Assert.AreEqual("http://www.domain.com/folder/app/file.aspx?a=hello", u.ToUrlType(UrlType.AbsoluteUri));
Assert.AreEqual("/folder/app/file.aspx?a=hello", u.ToUrlType(UrlType.RootRelative));
Assert.AreEqual("//www.domain.com/folder/app/file.aspx?a=hello", u.ToUrlType(UrlType.SchemeRelative));
// If in an ASP.NET context, the following will work as well
Assert.AreEqual("~/file.aspx?a=hello", u.ToUrlType(UrlType.AppRelative));

GetQueryItem Examples

In ASP.NET the Request object provides nice access to query string values, but this feature is noticeably absent from the Uri. This is partly because only certain scheme types actually support query items, but it would be nice to have easy access to query items from a Uri when you need it without having to write your own parsing code.

System.Web.HttpUtility.ParseQueryString(query) is a nice little method in the .NET framework that will extract the query string keys and values into a NameValueCollection. However, it is case insensitive which is an unfortunate assumption since
query items are actually case sensitive.

We've created our own static parsing routine on UriBuilderExtensions.ParseQueryItems(...). We use it under the covers and it
is optionally case sensitive. You can call it directly if you like.

But, to make this easier for you, Cambia.UriExtensions adds basic support for getting query string values through the GetQueryItem extension method.

NOTE: This method is not supported for relative URIs or schemes which don't support query strings. Obviously, this feature is most useful with http and https schemes.

Path Segments and Path Items Intro

The .NET Uri class does not offer much help when working with the Path portion of a URI.

That's why I've added a UriPath class to the Cambia.UriExtensions library. It provides an easy way to work
with path segments and path items

So, Steve. What are path segments and path items?

If you split a URI path on the slashes, then you end up with the segments. Unless, of course, you're the .NET Uri class. There is a Segments property on the Uri class. It's a string array, but it has a big problem in my opinion. It contains all of the slashes. The first segment is always a slash and subsequent segments contain a trailing slash.

What a pain when all you want is the content between the slashes.

NOTE: I understand why they designed the .NET Uri class they way they did. They wanted to have all the characters
which were present in the original URI to also be present in the various parts that are presented to the user.
You could simply join the segments and end up with the original path. This makes some sense for some scenarios. Unfortunately, in practice, I prefer my segments without slashes.

On to Path Items. I will admit that the term Path Item is something I've invented.

Path parameters was taken and means something a bit different.

With all the web frameworks today supporting URL routing in various forms, a lot more content
is being put in the URL path rather than in query items. It can make for cleaner URLs.

For example, a page number could be added in either a query string or in a path as follows:

http://www.domain.com/blog?page=2

http://www.domain.com/blog/page/2

If you'll look at that last URL where the page is a segment in the path, notice that page is like a key word and the 2 in the following segment is like the value.

This pair of adjacent segments is called a Path Item. The first segment is the key and the next one is a value.

That's it! That is the extent of the definition of a path item.

You'll see this definition has some consequences.

For example, blog/page is also a Path Item. In fact, all of the following pairs are Path Items:

{blog,page}

{page,2}

{2,}

Obviously, some of those are not key/value pairs and don't make sense. It is up to you, the user, to know
which pairs are key/value pairs and deal with them accordingly.

Drop and Set Chainable Methods

So far we've discussed three extensions methods: GetPart, ToUrlType and GetQueryItem. All of these are about extracting information from the URI or formatting it.

I would now like to introduce several methods which support building and modifying of URIs.

These methods form what's called a fluent interface which allows you to chain method calls.

Allow me to demonstrate.

Set Methods

Let's say we want to build a URL from scratch and we really don't want to mess with all those delimiters.

NOTE: UriBuilder is intended for building URIs. In truth, it's better than these extension methods if you are building a URI from scratch. These extension methods will be used more for easily modifying an existing Uri. But, you _could_ sort of build it from scratch as follows.

using Cambia;
// Build a URL using Set methods
// The Uri class requires that we start with a valid URI so we use the simplest
// thing we can.
Uri u = new Uri("junk://junk");
u = u.SetScheme("http")
.SetUser("steve")
.SetHost("host")
.SetPort(555)
.SetPath("/folder/app/file.aspx")
.SetQueryItem("a", "hello")
.SetQueryItem("b", "world")
.SetFragment("fraggle");
// Here's what we produced
Assert.AreEqual("http://steve@host:555/folder/app/file.aspx?a=hello&b=world#fraggle",
u.ToUrlType(UrlType.AbsoluteUri));
// And guess what, most of the inputs can contain leading and trailing delimiters (or not)
// so that you really don't have to worry about them. You can use Uri properties as
// inputs without having to remember whether the Uri.Fragment has a hash or not, etc.
u = u.SetScheme("http:")
.SetUser("steve:")
.SetHost("@host:")
.SetPort(555)
.SetPath("folder/app/file.aspx/")
.SetQueryItem("a", "hello")
.SetQueryItem("b", "world")
.SetFragment("#fraggle");
// The result is the same
Assert.AreEqual("http://steve@host:555/folder/app/file.aspx?a=hello&b=world#fraggle",
u.ToUrlType(UrlType.AbsoluteUri));

Drop Methods

The Drop methods are similar to the set methods, but instead of adding or modifying URI parts, they remove them. Only certain parts can be removed. Scheme and host for example are required elements of an absolute URI.

A Quick Word about Relative URIs

Support for relative URIs, those which have no scheme or authority, is minimal in the Uri class. Most of these extension methods described in this article won't work with relative URIs. If you fill a Uri object with a relative Uri, most of the normal operations and properties will throw exceptions.

Because of this, our extension methods are limited in the same ways as the Uri class itself. Many of these methods will throw exceptions when operating on URIs because the methods need to access parts or properties that are not supported by the Uri class.