runtime: [Uri] Uri.IsWellFormedUriString() returns false for a URL which is correct

I have a C# (.Net Core 1.1) app that needs to check if a URL is valid. I used the Uri.IsWellFormedUriString() which works pretty well but have a doubt about this one below which returns false. It seems to me that the URL is perfectly valid?

Uri.IsWellFormedUriString("http://www.test.com/search/Le+Venezuela+b%C3%A9n%C3%A9ficie+d%27importantes+ressources+naturelles+%3A+p%C3%A9trole%2C+gaz%2C+mines", UriKind.Absolute)

I used the very same URL with the PHP function below which says the URL is correctly formatted:

function filter_var($url, FILTER_VALIDATE_URL)

If I refer to the RFC3986 it seems this URL is correct. Am I missing something here?

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 19
  • Comments: 24 (12 by maintainers)

Commits related to this issue

Most upvoted comments

@karelz

I suspect the issue is related to combining encoded characters that require one encode value and characters that require multiple encode values. For example, 学 encodes to %E5%AD%A6 while [ encodes to %5B.

Here are some examples:

public class UriTests
    {
        [Fact] // Fails
        public void IsWellFormedUriString_ReturnTrue_GivenEncodedQueryStringWithCommaAndAccentCharacter()
        {
            var uri = @"http://g.c/j?a=%2C%C3%A9"; //encoded characters in query: ,é

            Assert.True(Uri.IsWellFormedUriString(uri, UriKind.Absolute));
        }

        [Fact] // Passes
        public void IsWellFormedUriString_ReturnTrue_GivenEncodedQueryStringWithComma()
        {
            var uri = @"http://g.c/j?a=%2C"; //encoded characters in query: ,

            Assert.True(Uri.IsWellFormedUriString(uri, UriKind.Absolute));
        }

        [Fact] // Passes
        public void IsWellFormedUriString_ReturnTrue_GivenEncodedQueryStringWithAccentCharacter()
        {
            var uri = @"http://g.c/j?a=%C3%A9"; //encoded characters in query: é

            Assert.True(Uri.IsWellFormedUriString(uri, UriKind.Absolute));
        }

        [Fact] // Fails
        public void IsWellFormedUriString_ReturnTrue_GivenEncodedQueryStringWithOpenBracketAndDoubleByteCharacter()
        {
            var uri = @"http://g.c/j?a=%E5%AD%A6%5B"; //encoded characters in query: 学[

            Assert.True(Uri.IsWellFormedUriString(uri, UriKind.Absolute));
        }

        [Fact] // Passes
        public void IsWellFormedUriString_ReturnTrue_GivenEncodedQueryStringWithOpenBracket()
        {
            var uri = @"http://g.c/j?a=%5B"; //encoded characters in query: [

            Assert.True(Uri.IsWellFormedUriString(uri, UriKind.Absolute));
        }

        [Fact] // Passes
        public void IsWellFormedUriString_ReturnTrue_GivenEncodedQueryStringWithDoubleByteCharacter()
        {
            var uri = @"http://g.c/j?a=%E5%AD%A6"; //encoded characters in query: 学

            Assert.True(Uri.IsWellFormedUriString(uri, UriKind.Absolute));
        }

    }

This URI is also failing on .NET 6.0

https://storage.googleapis.com/kocmoc/audio-archive/2021_10/2021_10_13 - The Last Dive with ΑΧΙΝΟΙ.mp3

@karelz .NET 6.0 was just released… having to wait for .NET 7 for a critical bug that should have been fixed 4 years ago is a bit ridiculous.

It has total 13 customer reports (1 was offline) – only 2 are upvotes of the top post (1 is the original post).

Can I ask everyone who’ve hit it to please upvote the top post? It will help us prioritize.

Moving it to .NET 7.0 as it has rather larger impact. cc @MihaZupan

In .net core 2.1 I am also encountering what looks to be the same bug, or a very similar bug.

var uri = @"https://maps.googleapis.com/maps/api/geocode/json?address=%2C%2CMontr%C3%A9al%2CQuebec%2CCanada&sensor=false";
            
Uri.IsWellFormedUriString(uri, UriKind.Absolute); //returns false, however above URI is valid.

However, if I leave the URI unencoded it passes the IsWellFormedUriString check:

var uri = @"https://maps.googleapis.com/maps/api/geocode/json?address=,,Montréal,Quebec,Canada&sensor=false";
            
Uri.IsWellFormedUriString(uri, UriKind.Absolute); //returns true

We also just encountered this issue.

And I just noticed this is up-for-grabs. A friend and I could be interested in doing this. @karelz everything good for a PR? And on the documentation part, whose responsibility would be that? The pr author, or you guys? I imagine the latter?

I just ran into this issue at work and can add that using non-ascii characters like Å or ตั together with any of the RFC 3986 section 2.2 Reserved Characters fails, ! * ’ ( ) ; : @ & = + $ , / ? # [ ]. E.g. Å*

I just ran into this one. You probably have plenty of examples, but just to further confirm @nicholasb90 's hypothesis:

Assert.True(Uri.IsWellFormedUriString("http://myhost.com/%26", UriKind.Absolute)); // pass
Assert.True(Uri.IsWellFormedUriString("http://myhost.com/%C3%A9", UriKind.Absolute)); //pass
Assert.True(Uri.IsWellFormedUriString("http://myhost.com/%26%C3%A9", UriKind.Absolute)); //fail

Is this a recommended work-around, i.e. using Uri.UnescapeDataString on the string before testing it? It makes my example pass but not sure if there are pitfalls.

As @svick said , I managed to overcome this issue by decoding the url.

string decodedUrl = HttpUtility.UrlEncode(url);
Uri.IsWellFormedUriString(decodedUrl, UriKind.RelativeOrAbsolute);

Try enabling IDN and IRI-Parsing in your App.config by adding this to your configuration section to ensure correct handling for international character set:

<uri>
<idn enabled="All"/>
<iriParsing enabled="true"/>
</uri>

Afer doing this, you should create a decoded version of your URL like this to avoid complications between encoded and decoded URLs:

string decodedURL = HttpUtility.UrlDecode(yourURLString);

Now you can check like this:

if (Uri.IsWellFormedUriString(yourURLString, UriKind.Absolute) || Uri.IsWellFormedUriString(decodedURL , UriKind.Absolute))

Maybe this is not a perfect solution, but the closest one for me to get this working as reliable as possible.

Btw. I’m using .Net Framework 4.5.2, but I guess it should also work with lower versions.