Header menu logo Navs

This library specializes itself in parsing URL-like strings into structured objects. It is used by Navs to parse navigable URLs and URL templates to find if they match.

Usage

For most use cases, you will want to use the existing RouteMatcher module. This module provides a single function matchStrings that takes a URL template and a URL and returns a Result type with the parsed URL template, the parsed URL, and the matched parameters.

open UrlTemplates.RouteMatcher

let template = "/api/v1/profiles/:id<guid>?activity<bool>#hash"

let url = $"/api/v1/profiles/{Guid.NewGuid()}?activity=false#profile"

let urlTemplate, urlInfo, urlMatch =
  match RouteMatcher.matchStrings template url with
  | Ok(urlTemplate, urlInfo, urlMatch) -> urlTemplate, urlInfo, urlMatch
  | Error errors -> failwithf "Failed to match URL: %A" errors

The urlTemplate is the parsed URL template, the urlInfo is the parsed URL, and the urlMatch is the matched parameters.

For example the contents of the URL template can be inspected and this information is what is ultimately used to determine if a URL matches a templated string.

{ Segments =
   [Plain ""; Plain "api"; Plain "v1"; Plain "profiles";
    ParamSegment ("id", Guid)]
  Query = [Optional ("activity", Bool)]
  Hash = ValueSome "hash" }

For the above case it means that URLs will be matched as long as they contain the /api/v1/profiles/ prefix, followed by a GUID, and an optional query parameter activity that is a boolean. The URL can also contain a hash.

For the case of the urlInfo it contains the parsed URL as a plain string, it doesn't contain any information about the matched parameters.

{ Segments =
   [""; "api"; "v1"; "profiles"; "1e1a2999-fbe4-4179-b1b8-176c0a1499ec"]
  Query = seq [[activity, String (ValueSome "false")]]
  Hash = ValueSome "profile" }

Finally, the urlMatch contains the matched parameters if they were supplied in the URL. For the above case, it will contain the matched GUID and the matched boolean.

Segments and Params: 1e1a2999-fbe4-4179-b1b8-176c0a1499ec

Query Params: activity=false

Hash: ValueSome "profile"

Providing Types for Parameters

Given that parameters can be matched against a type, it is possible to provide some of the Well known types included in TypedParam The general syntax is paramName<type> where type is one of the following:

For parameters where the name is not specified, the automatic type will be string.

Names must be alphanumeric strings and may contain - and _ characters.

Segment params

To specify a type for a segment parameter, use the following syntax:

For example:

Please note that every segment parameter is required even the trailing one.

Query params

To specify a type for a query parameter, use the following syntax:

For example:

In this case, the name query parameter is required and must be a string, and the age query parameter is optional but if suipplied it must be an integer.

Hash

The hash is a ValueOption string and it is specified as follows:

For example:

Extracting Parameters from a Matched URL

The UrlMatch type provides a set of functions to extract parameters from a matched URL.

let tplDefinition = "/api?name&age<int>&statuses"
let targetUrl = "/api?name=john&age=30&statuses=active&statuses=inactive"

let _, _, matchedUrl =
  RouteMatcher.matchStrings tplDefinition targetUrl
  |> Result.defaultWith(fun e -> failwithf "Expected Ok, got Error %O" e)

Normally query parameters are stored in string, object read only dictionaries however we offer a few utilities to extract them into a more useful format. for de F# folks you can use the following functions:

let name = matchedUrl |> UrlMatch.getFromParams<string> "name"

let age = matchedUrl |> UrlMatch.getFromParams<int> "age"

let values = matchedUrl |> UrlMatch.getParamSeqFromQuery<string> "statuses"

let statuses =
  values |> ValueOption.defaultWith(fun _ -> failwith "Expected a value")
seq ["inactive"; "active"]

For the non F# folks extension methods are also provided

var name = urlMatch.GetFromParams<string>("name");

var age = urlMatch.GetFromParams<int>("age");

var values = urlMatch.GetParamSeqFromQuery<string>("statuses");

if (values.IsNone)
{
  throw new Exception("Expected a value");
}

Console.WriteLine($"{values.Value[0]}, {values.Value[1]}");
// inactive, active

Note: The UrlMatch.getParamSeqFromQuery function and its extension method counterpart does not guarantee that the values are in the same order as they were in the URL.

namespace System
namespace UrlTemplates
namespace UrlTemplates.RouteMatcher
val template: string
val url: string
Multiple items
[<Struct>] type Guid = new: b: byte array -> unit + 6 overloads member CompareTo: value: Guid -> int + 1 overload member Equals: g: Guid -> bool + 1 overload member GetHashCode: unit -> int member ToByteArray: unit -> byte array + 1 overload member ToString: unit -> string + 2 overloads member TryFormat: destination: Span<char> * charsWritten: byref<int> * ?format: ReadOnlySpan<char> -> bool + 1 overload member TryWriteBytes: destination: Span<byte> -> bool + 1 overload static member (<) : left: Guid * right: Guid -> bool static member (<=) : left: Guid * right: Guid -> bool ...
<summary>Represents a globally unique identifier (GUID).</summary>

--------------------
Guid ()
Guid(b: byte array) : Guid
Guid(b: ReadOnlySpan<byte>) : Guid
Guid(g: string) : Guid
Guid(b: ReadOnlySpan<byte>, bigEndian: bool) : Guid
Guid(a: int, b: int16, c: int16, d: byte array) : Guid
Guid(a: int, b: int16, c: int16, d: byte, e: byte, f: byte, g: byte, h: byte, i: byte, j: byte, k: byte) : Guid
Guid(a: uint32, b: uint16, c: uint16, d: byte, e: byte, f: byte, g: byte, h: byte, i: byte, j: byte, k: byte) : Guid
Guid.NewGuid() : Guid
val urlTemplate: UrlTemplate.UrlTemplate
val urlInfo: UrlParser.UrlInfo
val urlMatch: UrlMatch
module RouteMatcher from UrlTemplates.RouteMatcher
val matchStrings: template: string -> url: string -> FsToolkit.ErrorHandling.Validation<(UrlTemplate.UrlTemplate * UrlParser.UrlInfo * UrlMatch),StringMatchError>
<summary> Matches a URL against a templated string </summary>
<param name="template">The template to match the URL against</param>
<param name="url">The URL to match</param>
<returns> A validation that contains the UrlTemplate obtained from the provided string, the matched URL, and the match result if the URL matches the template or a list of StringMatchErrors if there were problems parsing the template, the URL or if the URL doesn't match the template. </returns>
union case Result.Ok: ResultValue: 'T -> Result<'T,'TError>
union case Result.Error: ErrorValue: 'TError -> Result<'T,'TError>
val errors: StringMatchError list
val failwithf: format: Printf.StringFormat<'T,'Result> -> 'T
val printfn: format: Printf.TextWriterFormat<'T> -> 'T
UrlMatch.Params: Collections.Generic.IReadOnlyDictionary<string,obj>
<summary> The matched segments of the URL, this will be used to extract the parameters </summary>
<example> /:id/:name will match /123/john and the segments will be Params["id"] = 123 Params["name"] = "john" </example>
module Seq from Microsoft.FSharp.Collections
val map: mapping: ('T -> 'U) -> source: 'T seq -> 'U seq
active recognizer KeyValue: Collections.Generic.KeyValuePair<'Key,'Value> -> 'Key * 'Value
val k: string
val v: obj
Multiple items
type String = interface IEnumerable<char> interface IEnumerable interface ICloneable interface IComparable interface IComparable<string> interface IConvertible interface IEquatable<string> interface IParsable<string> interface ISpanParsable<string> new: value: nativeptr<char> -> unit + 8 overloads ...
<summary>Represents text as a sequence of UTF-16 code units.</summary>

--------------------
String(value: nativeptr<char>) : String
String(value: char array) : String
String(value: ReadOnlySpan<char>) : String
String(value: nativeptr<sbyte>) : String
String(c: char, count: int) : String
String(value: nativeptr<char>, startIndex: int, length: int) : String
String(value: char array, startIndex: int, length: int) : String
String(value: nativeptr<sbyte>, startIndex: int, length: int) : String
String(value: nativeptr<sbyte>, startIndex: int, length: int, enc: Text.Encoding) : String
val concat: sep: string -> strings: string seq -> string
UrlMatch.QueryParams: Collections.Generic.IReadOnlyDictionary<string,obj>
<summary> The matched query parameters of the URL, this will be used to extract the parameters </summary>
<example> /users?name&amp;age&lt;int&gt; will match /users?name=john&amp;age=30 QueryParams["name"] = "john" QueryParams["age"] = 30 </example>
UrlMatch.Hash: string voption
<summary> The hash of the URL </summary>
<example> /users#123 will match /users#123 Hash = ValueSome "123" </example>
val tplDefinition: string
val targetUrl: string
val matchedUrl: UrlMatch
Multiple items
module Result from Microsoft.FSharp.Core

--------------------
[<Struct>] type Result<'T,'TError> = | Ok of ResultValue: 'T | Error of ErrorValue: 'TError
val defaultWith: defThunk: ('Error -> 'T) -> result: Result<'T,'Error> -> 'T
val e: StringMatchError list
val name: string voption
Multiple items
module UrlMatch from UrlTemplates.RouteMatcher

--------------------
type UrlMatch = { Params: IReadOnlyDictionary<string,obj> QueryParams: IReadOnlyDictionary<string,obj> Hash: string voption }
<summary> The result of a successful match between a URL and a templated URL </summary>
val getFromParams: name: string -> urlMatch: UrlMatch -> 'CastedType voption
<summary> Gets a parameter from the query parameters or segments of the URL </summary>
<param name="name">The name of the parameter to get</param>
<param name="urlMatch">The match result to get the parameter from</param>
<returns> The parameter value if it exists in the query parameters or path segments and it was succesfully parsed to it's supplied type or None if it doesn't </returns>
Multiple items
val string: value: 'T -> string

--------------------
type string = String
val age: int voption
Multiple items
val int: value: 'T -> int (requires member op_Explicit)

--------------------
type int = int32

--------------------
type int<'Measure> = int
val values: string seq voption
val getParamSeqFromQuery: name: string -> urlMatch: UrlMatch -> 'CastedType seq voption
<summary> Gets a sequence of parameters from the supplied query key in the URL </summary>
<param name="name">The name of the parameter to get</param>
<param name="urlMatch">The match result to get the parameter from</param>
<returns> The parameter values if it exists in the query parameters and it was succesfully parsed to it's supplied type or None if it doesn't </returns>
val statuses: string seq
Multiple items
module ValueOption from Microsoft.FSharp.Core

--------------------
[<Struct>] type ValueOption<'T> = | ValueNone | ValueSome of 'T static member Some: value: 'T -> 'T voption static member op_Implicit: value: 'T -> 'T voption member IsNone: bool member IsSome: bool member Value: 'T static member None: 'T voption
val defaultWith: defThunk: (unit -> 'T) -> voption: 'T voption -> 'T
val failwith: message: string -> 'T

Type something to start searching.