UrlMatcher 3.1.0

dotnet add package UrlMatcher --version 3.1.0
                    
NuGet\Install-Package UrlMatcher -Version 3.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="UrlMatcher" Version="3.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="UrlMatcher" Version="3.1.0" />
                    
Directory.Packages.props
<PackageReference Include="UrlMatcher" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add UrlMatcher --version 3.1.0
                    
#r "nuget: UrlMatcher, 3.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package UrlMatcher@3.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=UrlMatcher&version=3.1.0
                    
Install as a Cake Addin
#tool nuget:?package=UrlMatcher&version=3.1.0
                    
Install as a Cake Tool

alt tag

UrlMatcher

NuGet Version NuGet

Simple URL matcher library allowing you to match based on explicit strings, parameters, and catch-all segments, targeted to .NET Standard 2.0/2.1, .NET Framework 4.6.2/4.8, and .NET 8.0/10.0.

Help or Feedback

First things first - do you need help or have feedback? File an issue here! We'd love to hear from you.

New in v3.1.0

  • Catch-all segments: /api/{*rest} matches /api, /api/users, and /api/users/42/orders
  • UrlPattern parses a pattern once for reuse, reports its shape for route ranking, and rejects invalid patterns up front
  • A failed match always returns an empty collection (3.0.2 could leave values captured before the failing segment)

See CHANGELOG.md for compatibility notes.

Install

dotnet add package UrlMatcher

Usage

Static Method

Use the static method when you need to compare an input URL against a pattern once.

using System.Collections.Specialized;
using UrlMatcher;

if (Matcher.Match("/v1.0/users/42", "/{version}/users/{userId}", out NameValueCollection vals))
{
    Console.WriteLine("version : " + vals["version"]);  // v1.0
    Console.WriteLine("userId  : " + vals["userId"]);   // 42
}

Instance Method

Instantiate the class with a URL or Uri to parse it once, then match it against many patterns.

Matcher matcher = new Matcher("/v1.0/users/42");

if (matcher.Match("/{version}/users/{userId}", out NameValueCollection vals))
{
    // vals["userId"] == "42"
}
else if (matcher.Match("/{version}/hobbies/{hobbyId}", out vals))
{
    // ...
}

Catch-All Segments

A catch-all is written {*name} and must be the entire final segment of the pattern. It matches zero or more remaining URL segments and captures them as the raw remainder of the URL.

Matcher.Match("/api/users/42/orders", "/api/{*rest}", out NameValueCollection vals);  // true, rest = "users/42/orders"
Matcher.Match("/api", "/api/{*rest}", out vals);                                     // true, rest = ""
Matcher.Match("/v1/files/a/b.txt", "/{v}/files/{*path}", out vals);                  // true, v = "v1", path = "a/b.txt"
Matcher.Match("/other/users", "/api/{*rest}", out vals);                             // false

The captured value is the remainder of the URL exactly as supplied (after the query string and fragment are removed), so it is safe to substitute into a forwarded path:

URL Pattern rest
/api/a//b /api/{*rest} a//b (repeated slashes kept)
/api/a/b/ /api/{*rest} a/b/ (trailing slash kept)
/api/a%2Fb/c /api/{*rest} a%2Fb/c (not decoded)
/api/a/b?x=1#f /api/{*rest} a/b (query and fragment removed)

A catch-all that is not the last segment, appears more than once, or shares its segment with other text (for example /{*rest}/edit, /{*a}/{*b}, or /files/v{*x}) throws ArgumentException.

Pre-Parsed Patterns

UrlPattern parses a pattern once. Routers should parse patterns when routes are registered, so an invalid pattern fails immediately instead of on every request, and matching skips re-parsing.

UrlPattern pattern = UrlPattern.Parse("/{v}/files/{*path}");   // throws ArgumentException if invalid

if (Matcher.Match("/v1/files/a/b.txt", pattern, out NameValueCollection vals)) { ... }
if (matcher.Match(pattern, out vals)) { ... }

if (!UrlPattern.TryParse(userSupplied, out UrlPattern parsed)) { /* reject the route */ }

UrlPattern also reports facts a router can use to rank routes: SegmentCount, FixedSegmentCount, LiteralCount, ParameterCount, LiteralPrefixCount, IsCatchAll, CatchAllName, and Segments (each with Text, Type, and Name). The ranking policy itself is left to the router.

Matching Rules

  • URLs and patterns are split on / and empty segments are discarded, so leading, trailing, and repeated slashes do not matter (except inside a catch-all value).
  • The query string and fragment are removed from the URL before matching. They are not removed from the pattern.
  • The URL and pattern must have the same number of segments, unless the pattern ends in a catch-all.
  • Literal segments are compared ordinally (case-sensitive, no culture or Unicode normalization).
  • Parameter names are case-insensitive. Captured values keep their case and are not URL-decoded.
  • A failed match returns false and an empty (never null) collection.
  • Duplicate parameter names are joined with a comma by NameValueCollection (/{id}/{id} against /a/b gives id = a,b).

Pattern Syntax

Form Example pattern Matches (captured values) Does not match
{name} /users/{id} /users/42 (id=42) /users, /users/42/orders
{*name} as the whole last segment /api/{*rest} /api/users/42 (rest=users/42), /api (rest="") /other/users, /API/users
{*name} not last /{*rest}/edit throws ArgumentException
More than one {*name} /{*a}/{*b} throws ArgumentException
{*name} with other text in the segment /files/v{*x} throws ArgumentException
{*} /files/{*} /files/{*} (literal) /files/42
{} /files/{} /files/{} (literal) /files/42
* or ** /files/* /files/* (literal) /files/a, /files/a/b
Unclosed brace /users/{id /users/{id (literal) /users/42
Literal /Users/list /Users/list /users/list

Some brace forms are kept for compatibility with earlier versions and are best avoided:

Form Example pattern Behavior
Braces inside a segment /v{version}/users /v1/users gives version=v1 (the whole segment, not 1)
Two groups in one segment /item/{a}{b} /item/xy gives a=xy; b is never set
Spaces in a name /users/{ id } The key is id, so vals["id"] is null
Query text in a pattern /search?q={q} One segment; /search?q=1 gives q=search

Thread Safety

Matcher, UrlPattern, and UrlPatternSegment instances are immutable after construction and safe to use from multiple threads concurrently. Each call to Match returns a new collection.

Testing

Tests use Touchstone. Test descriptors live in src/Test.Shared and run through three runners:

dotnet run --project src/Test.Automated -- --results results.json
dotnet test src/Test.Xunit
dotnet test src/Test.Nunit

Version History

Please refer to CHANGELOG.md.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 is compatible. 
.NET Framework net461 was computed.  net462 is compatible.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 is compatible.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETFramework 4.6.2

    • No dependencies.
  • .NETFramework 4.8

    • No dependencies.
  • .NETStandard 2.0

    • No dependencies.
  • .NETStandard 2.1

    • No dependencies.
  • net10.0

    • No dependencies.
  • net8.0

    • No dependencies.

NuGet packages (4)

Showing the top 4 NuGet packages that depend on UrlMatcher:

Package Downloads
Watson

Simple, fast, async C# web server for handling REST requests with unified 7.x protocol architecture targeting .NET Standard 2.1, .NET 8, and .NET 10.

Watson.Core

Core library for Watson 7.x shared semantics, routing, lifecycle, and protocol configuration targeting .NET 8 and .NET 10.

HttpServerLite

User-space HTTP and HTTPS server without http.sys written in C#. Please visit the project site README for details on implementation.

OllamaFlow.Core

OllamaFlow is a lightweight intelligent load-balancer for Ollama.

GitHub repositories (1)

Showing the top 1 popular GitHub repositories that depend on UrlMatcher:

Repository Stars
dotnet/WatsonWebserver
Watson is the fastest, easiest way to build scalable RESTful web servers and services in C#.
Version Downloads Last Updated
3.1.0 0 9/29/2026
3.0.2 14,280 9/29/2025
3.0.1 69,923 1/7/2025
3.0.0 10,720 8/2/2024
2.0.1 83,661 3/13/2023
2.0.0 896 3/13/2023
1.0.0.3 131,175 2/7/2022
1.0.0.2 23,702 11/12/2021
1.0.0.1 87,351 2/22/2021
1.0.0 1,142 2/22/2021

Catch-all segments ({*name}), UrlPattern for pre-parsed patterns, failed matches always return an empty collection.