90

Are there any helper classes available in .NET to allow me to build a Url?

For example, if a user enters a string:

stackoverflow.com

and i try to pass that to an HttpWebRequest:

WebRequest.CreateHttp(url);

It will fail, because it is not a valid url (it has no prefix).

What i want is to be able to parse the partial url the user entered:

Uri uri = new Uri(url);

and then fix the missing pieces:

if (uri.Port == 0)
   uri.Port = 3333;
if (uri.Scheme == "")
   uri.Scheme = "https";

Does .NET have any classes that can be used to parse and manipulate Uri's?

The UriBuilder class can't do the job

The value that the user entered (e.g. stackoverflow.com:3333) is valid; i just need a class to pick it apart. i tried using the UriBuilder class:

UriBuilder uriBuilder = new UriBuilder("stackoverflow.com:3333");

unfortunately, the UriBuilder class is unable to handle URIs:

  • uriBuilder.Path = 3333
  • uriBuilder.Port = -1
  • uriBuidler.Scheme = stackoverflow.com

So i need a class that can understand host:port, which especially becomes important when it's not particularly http, but could be.

Bonus Chatter

Console application.

From the other question

Some examples of URL's that require parsing:

  • server:8088
  • server:8088/func1
  • server:8088/func1/SubFunc1
  • http://server
  • http://server/func1
  • http://server/func/SubFunc1
  • http://server:8088
  • http://server:8088/func1
  • http://server:8088/func1/SubFunc1
  • magnet://server
  • magnet://server/func1
  • magnet://server/func/SubFunc1
  • magnet://server:8088
  • magnet://server:8088/func1
  • magnet://server:8088/func1/SubFunc1
  • http://[2001:db8::1]
  • http://[2001:db8::1]:80

The format of a Url is:

  foo://example.com:8042/over/there?name=ferret#nose
  \_/   \_________/ \__/\_________/\__________/ \__/
   |         |        |     |           |        |
scheme      host    port   path       query   fragment

Bonus Chatter

Just to point out again that UriBuilder does not work:

Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
  • 1
    It looks like `UriBuilder` handles this correctly - `stackoverflow.com:3333` is a URI with scheme `stackoverflow.com` and path `3333`. If you want something to parse it differently you'll have to specify how you want the behaviour to differ. You might just be able to assume an unknown scheme is a hostname, or write your own [UriParser](https://learn.microsoft.com/en-us/dotnet/api/system.uriparser?view=netframework-4.8). – Lee Apr 21 '20 at 21:37
  • The problem is that the scheme is not `stackoverflow.com`. Just ask Lynx, Netscape Navigator, Internet Explorer, FireFox, Chrome, Edge, and the [living standard](https://url.spec.whatwg.org/#url-parsing). They all realize that `stackoverflow.com` is the host. I want that Uri parser that every practical real-world parser understands. If you're suggesting i write my own, that's fine - see [this question](https://stackoverflow.com/questions/20164298) where i ask how to do it. Ideally the class already exists in the FCL. – Ian Boyd Apr 22 '20 at 14:18
  • 1
    Your "format of a URL" contradicts the specification. See https://www.rfc-editor.org/rfc/rfc3986#section-1.1.1 and https://www.rfc-editor.org/rfc/rfc3986#section-3 Note also in 4.2 "A path segment that contains a colon character (e.g., "this:that") cannot be used as the first segment of a relative-path reference, as it would be mistaken for a scheme name." – Ben Voigt Sep 07 '22 at 15:35
  • @BenVoigt I know it violates the spec. That is why i am looking for code that can build a URL entered by a user. User's don't care about the RFC, and neither do i. – Ian Boyd Sep 07 '22 at 15:48
  • @IanBoyd: For your particular examples you can use `new UriBuilder(userInput.Contains("://")? userInput: $"defaultscheme:{userInput}")`. It will, however, make a mess of `mailto:` URIs – Ben Voigt Sep 07 '22 at 15:52
  • @BenVoigt And also websocket URIs. – Ian Boyd Sep 07 '22 at 16:01
  • @IanBoyd: Both `ws` and `wss` schemes use an authority introduced by `//`, so that code snippet should work fine with them. – Ben Voigt Sep 07 '22 at 16:04

3 Answers3

84

If you need to ensure that some string coming as user input is valid url you could use the Uri.TryCreate method:

Uri uri;
string someUrl = ...
if (!Uri.TryCreate(someUrl, UriKind.Absolute, out uri))
{
    // the someUrl string did not contain a valid url 
    // inform your users about that
}
else
{
    var request = WebRequest.Create(uri);
    // ... safely proceed with executing the request
}

Now if on the other hand you want to be building urls in .NET there's the UriBuilder class specifically designed for that purpose. Let's take an example. Suppose you wanted to build the following url: http://example.com/path?foo=bar&baz=bazinga#some_fragment where the bar and bazinga values are coming from the user:

string foo = ... coming from user input
string baz = ... coming from user input

var uriBuilder = new UriBuilder("http://example.com/path");
var parameters = HttpUtility.ParseQueryString(string.Empty);
parameters["foo"] = foo;
parameters["baz"] = baz;
uriBuilder.Query = parameters.ToString();
uriBuilder.Fragment = "some_fragment";

Uri finalUrl = uriBuilder.Uri;
var request = WebRequest.Create(finalUrl);
... safely proceed with executing the request
Grazerbeam
  • 345
  • 4
  • 17
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 3
    `HttpUtility.ParseQueryString` is a sneaky way to get an `HttpValueCollection` which is internal (that's why its `.ToString()` works like that). Not sure it's a good idea to use internal framework classes and rely on their undocumented behavior. Credit for this insight goes to https://codereview.stackexchange.com/questions/91783/constructing-a-query-string-using-stringbuilder#comment166606_91795 – Jesse Hufstetler Mar 05 '18 at 14:47
  • @JesseHufstetler - I think it is safe to use since the [MSDN article](https://learn.microsoft.com/en-us/dotnet/api/system.web.httputility.parsequerystring?view=netframework-4.8) about it doesn't mention any warnings, and has examples of usage. – bPratik Sep 27 '19 at 17:13
  • 1
    Note that UriBuilder chokes on [`stackoverflow.com:3389`](https://dotnetfiddle.net/s66kdZ) – Ian Boyd Apr 21 '20 at 21:01
20

You can use the UriBuilder class.

var builder = new UriBuilder(url);
builder.Port = 3333
builder.Scheme = "https";

var result = builder.Uri;
Lee
  • 142,018
  • 20
  • 234
  • 287
7

To be valid a URI needs to have the scheme component. "server:8088" is not a valid URI. "http://server:8088" is. See https://www.rfc-editor.org/rfc/rfc3986

Community
  • 1
  • 1
michael
  • 71
  • 1
  • 1
  • 1
    And yet your Chrome browser knows what i mean when you type in `stackoverflow.com:80`. What .NET class is able to parse URIs. – Ian Boyd Apr 08 '17 at 04:18
  • 2
    Actually, server:8080 is a perfectly valid URI, though probably nobody is going to understand what you mean with the server scheme. – Georg Apr 16 '17 at 14:25
  • 1
    always thought scheme would be `server://` not `server:port` but it is interesting to ponder basic rules of understanding – ljgww Feb 26 '18 at 07:41
  • 4
    @IanBoyd: Some browsers just know what the most common typo's are and adjust themselves to it ;-) – Stefan Oct 20 '18 at 15:43
  • 3
    @Ian: Chrome just detects the parsing failure and tries again, this time with `"https://" + userinput` That's not a URL manipulation, it's a string manipulation. – Ben Voigt Sep 07 '22 at 15:30