Add scheme to URL if needed

C#.NetUri

C# Problem Overview


To create a Uri from a string you can do this:

Uri u = new Uri("example.com");

But the problem is if the string (like the one above) doesn't contain the protocol you will get an exception: "Invalid URI: The format of the URI could not be determined."

To avoid the exception you should secure the string includes a protocol, like below:

Uri u = new Uri("http://example.com");

But if you take the url as input, how can you add the protocol if it's missing?
I mean apart from some IndexOf/Substring manipulation?

Something elegant and fast?

C# Solutions


Solution 1 - C#

You could also use UriBuilder:

public static Uri GetUri(this string s)
{
    return new UriBuilder(s).Uri;
}

Remarks from MSDN:

> This constructor initializes a new instance of the UriBuilder class with the Fragment, Host, Path, Port, Query, Scheme, and Uri properties set as specified in uri. > > If uri does not specify a scheme, the scheme defaults to "http:".

Solution 2 - C#

If you just want to add the scheme, without validating the URL, the fastest/easiest way is to use string lookups, eg:

string url = "mydomain.com";
if (!url.StartsWith("http://", StringComparison.OrdinalIgnoreCase)) url = "http://" + url;

A better approach would be to use Uri to also validate the URL using the TryCreate method:

string url = "mydomain.com";
Uri uri;
if ((Uri.TryCreate(url, UriKind.Absolute, out uri) || Uri.TryCreate("http://" + url, UriKind.Absolute, out uri)) &&
	(uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps))
{
	// Use validated URI here
}

As @JanDavidNarkiewicz pointed out in the comments, validating the Scheme is necessary to guard against invalid schemes when a port is specified without scheme, e.g. mydomain.com:80.

Solution 3 - C#

My solution was for protocall-less urls to make sure they have protocal was regex :

Regex.Replace(s, @"^\/\/", "http://");

Solution 4 - C#

Interestingly, although Uri and UriBuilder completely mangle any url without a scheme, WebProxy does it right.

So just call:

new WebProxy(proxy.ProxyServer).Address

Solution 5 - C#

The accepted answer results in http and not https. I've found this answer and built up on it

namespace UrlHelper
{
    using System;

    public class UrlExtensions
    {
        public static Uri AddHttpsPrefixToString(string url)
        {
            if (string.IsNullOrWhiteSpace(url))
            {
                return null;
            }

            Uri uri;
            try
            {
                uri = new UriBuilder(url) {
                    Scheme = Uri.UriSchemeHttps,
                    Port = -1 // default port for scheme
                }.Uri;
            }
            catch
            {
                Console.WriteLine($"UrlExtensions.AddHttpsPrefixToString: Could not parse {url}");
                return null;
            }

            return uri;
        }
    }
}

Use it like this: var termsStringOrNull = UrlExtensions.AddHttpsPrefixToString("example.com")?.ToString()

Solution 6 - C#

We had some specific cases where there was a legacy allowance to input stuff like: localhost:8800 or similar. Which means we needed to parse that. We built a little more elaborate ParseUri method that separated the possibility to specify a URI very loosely, but also caught the times where people would specify a non-standard scheme (and also the host in IP-long notation, because sometimes people do that)

Just like UriBuilder it will default to the http scheme if none is specified. It will have issues if a basic authentication is specified and the password consists only of numbers. (Feel free to fix that community)

        private static Uri ParseUri(string uri)
        {

            if (uri.StartsWith("//"))
                return new Uri("http:" + uri);
            if (uri.StartsWith("://"))
                return new Uri("http" + uri);

            var m = System.Text.RegularExpressions.Regex.Match(uri, @"^([^\/]+):(\d+)(\/*)", System.Text.RegularExpressions.RegexOptions.IgnoreCase | System.Text.RegularExpressions.RegexOptions.Singleline);
            if (m.Success)
            {
                var port = int.Parse(m.Groups[2].Value);
                if (port <= 65535) //part2 is a port (65535 highest port number)
                    return new Uri("http://" + uri);
                else if (port >= 16777217) //part2 is an ip long (16777217 first ip in long notation)
                    return new UriBuilder(uri).Uri;
                else
                    throw new ArgumentOutOfRangeException("Invalid port or ip long, technically could be local network hostname, but someone needs to be hit on the head for that one");
            }
            else
                return new Uri(uri);
        }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMax FavilliView Question on Stackoverflow
Solution 1 - C#as-ciiView Answer on Stackoverflow
Solution 2 - C#RonaldView Answer on Stackoverflow
Solution 3 - C#JohnView Answer on Stackoverflow
Solution 4 - C#Aaron QueenanView Answer on Stackoverflow
Solution 5 - C#CodingYourLifeView Answer on Stackoverflow
Solution 6 - C#PeterView Answer on Stackoverflow