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.
|
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.
|
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.
|
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:
int
float
bool
guid
long
decimal
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:
/:paramName<type>
For example:
/api/v1/users/:id<guid>/items/:itemId<int>
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:
?paramName<type>
- for optional query parameters?paramName<type>!
- for required query parameters
For example:
/?name!&age<int>
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:
#hash
For example:
/docs/functions#callbacks
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")
|
For the non F# folks extension methods are also provided
|
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.
[<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
<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>
<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>
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
<summary> The matched query parameters of the URL, this will be used to extract the parameters </summary>
<example> /users?name&age<int> will match /users?name=john&age=30 QueryParams["name"] = "john" QueryParams["age"] = 30 </example>
<summary> The hash of the URL </summary>
<example> /users#123 will match /users#123 Hash = ValueSome "123" </example>
module Result from Microsoft.FSharp.Core
--------------------
[<Struct>] type Result<'T,'TError> = | Ok of ResultValue: 'T | Error of ErrorValue: 'TError
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>
<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>
val string: value: 'T -> string
--------------------
type string = String
val int: value: 'T -> int (requires member op_Explicit)
--------------------
type int = int32
--------------------
type int<'Measure> = int
<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>
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