Skip to main content
net10.0net11.0

Helpers to manipulate Uri

Published
Aug 29, 2026
1mo ago
Total Downloads
78.4K
78,428

Install

dotnet add package Meziantou.Framework.Uri --version 2.0.3
ProductVersions
.NET
net10.0net11.0

README

Meziantou.Framework.Uri

Meziantou.Framework.Uri provides methods to manipulate URIs.

URL Pattern API

Implementation of the WHATWG URL Pattern API for matching URLs against patterns.

// Create a pattern from a URL pattern init
var pattern = UrlPattern.Create(new UrlPatternInit
{
    Protocol = "https",
    Hostname = "example.com",
    Pathname = "/books/:id",
});

// Test if a URL matches the pattern
bool matches = pattern.IsMatch("https://example.com/books/123"); // true

// Create a pattern from a pattern string
var pattern2 = UrlPattern.Create("https://example.com/api/:version/*");

// Use wildcards
var wildcardPattern = UrlPattern.Create(new UrlPatternInit
{
    Pathname = "/files/*",
});
wildcardPattern.IsMatch("https://example.com/files/a/b/c"); // true

// Use modifiers (optional, one-or-more, zero-or-more)
var optionalPattern = UrlPattern.Create(new UrlPatternInit
{
    Pathname = "/items{/:category}?",
});
optionalPattern.IsMatch("https://example.com/items"); // true
optionalPattern.IsMatch("https://example.com/items/books"); // true

// Case-insensitive matching
var caseInsensitivePattern = UrlPattern.Create(
    new UrlPatternInit { Pathname = "/Books/:id" },
    new UrlPatternOptions { IgnoreCase = true });
caseInsensitivePattern.IsMatch("https://example.com/BOOKS/123"); // true

// Execute pattern and capture groups
var pattern3 = UrlPattern.Create(new UrlPatternInit
{
    Pathname = "/users/:userId/posts/:postId",
});
UrlPatternResult? result = pattern3.Match("https://example.com/users/42/posts/99");
if (result != null)
{
    string? userId = result.Pathname.Groups["userId"]; // "42"
    string? postId = result.Pathname.Groups["postId"]; // "99"
}

UrlPatternCollection

Match URLs against multiple patterns:

var collection = new UrlPatternCollection
{
    UrlPattern.Create(new UrlPatternInit { Pathname = "/api/*" }),
    UrlPattern.Create(new UrlPatternInit { Pathname = "/docs/*" }),
};

// Test if any pattern matches
bool anyMatch = collection.IsMatch("https://example.com/api/users"); // true

// Get the first matching pattern
UrlPattern? match = collection.FindPattern("https://example.com/docs/guide"); // returns the /docs/* pattern

// Match and capture groups from the first matching pattern
var apiCollection = new UrlPatternCollection
{
    UrlPattern.Create(new UrlPatternInit { Pathname = "/api/:version/*" }),
};
UrlPatternResult? apiResult = apiCollection.Match("https://example.com/api/v2/users");
if (apiResult != null)
{
    string? version = apiResult.Pathname.Groups["version"]; // "v2"
}

Query String Utilities

Parse and edit query strings

var uri = "https://www.meziantou.net";
var query = QueryStringUtilities.ParseQueryString(uri);
var author = query["author"];

_ = QueryStringUtilities.AddQueryString(uri, "name", "value");
_ = QueryStringUtilities.AddOrReplaceQueryString(uri, "name", "value");
_ = QueryStringUtilities.SetQueryString(uri, "name", "value");

// Remove a query string parameter
_ = QueryStringUtilities.AddQueryString(uri, "name", null);

Public Suffix List

A snapshot of the Public Suffix List is compiled into the assembly and used to find the public suffix (eTLD) and the registrable domain (eTLD+1) of a domain name. No network access is performed; the list is refreshed when a new version of the package is released.

// Get the registrable domain (eTLD+1)
_ = PublicSuffixList.GetRegistrableDomain("www.example.co.uk"); // example.co.uk
_ = PublicSuffixList.GetRegistrableDomain("co.uk");             // null

// Get the public suffix (eTLD)
_ = PublicSuffixList.GetPublicSuffix("www.example.co.uk"); // co.uk

// Test if a domain is a public suffix
_ = PublicSuffixList.IsPublicSuffix("co.uk");       // true
_ = PublicSuffixList.IsPublicSuffix("example.com"); // false

// Decompose a domain name
if (PublicSuffixList.TryGetDomainInfo("www.a.example.co.uk", out DomainInfo info))
{
    _ = info.PublicSuffix;         // co.uk
    _ = info.RegistrableDomain;    // example.co.uk
    _ = info.Subdomain;            // www.a
    _ = info.Source;               // PublicSuffixRuleSources.Icann
    _ = info.IsKnownPublicSuffix;  // true
}

// A Uri can be used directly
_ = PublicSuffixList.TryGetDomainInfo(new Uri("https://www.example.co.uk/path"), out _);

The list has two sections: the suffixes delegated by ICANN, and the suffixes submitted by domain holders such as blogspot.com. Both are used by default; the matching can be restricted to one of them.

// blogspot.com is a private rule
_ = PublicSuffixList.GetRegistrableDomain("foo.blogspot.com");                                   // foo.blogspot.com
_ = PublicSuffixList.GetRegistrableDomain("foo.blogspot.com", PublicSuffixRuleSources.Icann);    // blogspot.com

When a domain matches no rule of the list, the implicit * rule applies and the top-level domain is the public suffix. DomainInfo.IsKnownPublicSuffix reports whether an actual rule matched.

Changes from Previous Version

No structural changes between v2.0.2 and v2.0.3.

Adoption Assessment

A verified publisher signal is available. Assess fit from the package's supported frameworks, license and dependency graph rather than popularity alone.

Compatibility metadata lists 2 target frameworks; confirm the exact target used by your application.

Related Guides & Workflows

Learn how to evaluate, integrate, or audit this package in your .NET project.