As of version 1.7.0 URI.js includes an implementation of URI Templates, as specified in RFC 6570 (Level 4, March 2012).
// creating a new URI Template var template = new URITemplate("http://example.org/{file}"); var result = template.expand({file: "hello world.html"}); result === "http://example.org/hello%20world.html"; // of course you can call the constructor like a function and chain things: result = URITemplate("http://example.org/{file}") .expand({file: "hello world.html"}); result === "http://example.org/hello%20world.html"; // access via URI result = URI.expand("http://example.org/{file}", {file: "hello world.html"}); // result == new URI("http://example.org/hello%20world.html"); // expand() accepts data-callbacks: template.expand(function(key) { var data = {file: "hello world.html"}; return data[key]; }); // expand() accepts key-callbacks: template.expand({file : function(key) { return "hello world.html"; }}); // Using strict mode var template = new URITemplate("http://example.org/{file}"); var result = template.expand({filename: "hello world.html"}, { strict: true }); // Uncaught Error: Missing expansion value for variable "file"
Expressions are placeholders which are to be substituted by the values their variables reference.
http://example.org/~{username}/
http://example.org/dictionary/{term:1}/{term}
http://example.org/search{?q*,lang}
An expression consists of an operator and a (comma-separated) list of variable-specifications. A variable-specification consists of a variable and an optional modifier.
Given the template
http://example.org/~{username}/{term:1}/{term}{?q*,lang}
and the following data:
{username: "rodneyrehm", term: "hello world", q: {a: "mars", b: "jupiter"}, lang: "en"}
the expansion looks as follows:
"http://example.org/~rodneyrehm/h/hello%20world?a=mars&b=jupiter&lang=en"
List of supported operators:
Operator | Description |
---|---|
None | Simple String Expansion; |
+ | Reserved character strings; |
# | Fragment identifiers prefixed by "#"; |
. | Name labels or extensions prefixed by "."; |
/ | Path segments prefixed by "/"; |
; | Path parameter name or name=value pairs prefixed by ";"; |
? | Query component beginning with "?" and consisting of name=value pairs separated by "&"; and, |
& | Continuation of query-style &name=value pairs within a literal query component. |
List of supported modifiers:
Modifier | Description |
---|---|
None | No modification, arrays and objects are joined with "," |
* | Explode arrays and objects (see tables below) |
:3 | Substring of the first 3 characters of the variable's value |
Given {"var": "hello[world]"}
, the expression {var}
expands to hello%5Bworld%5D
.
The following table shows an output matrix for every possible operator/modifier combination produced for string
input.
Modifier | |||
---|---|---|---|
Operator | None | * | :2 |
None | hello%5Bworld%5D | hello%5Bworld%5D | he |
+ | hello[world] | hello[world] | he |
# | #hello[world] | #hello[world] | #he |
. | .hello%5Bworld%5D | .hello%5Bworld%5D | .he |
/ | /hello%5Bworld%5D | /hello%5Bworld%5D | /he |
; | ;var=hello%5Bworld%5D | ;var=hello%5Bworld%5D | ;var=he |
? | ?var=hello%5Bworld%5D | ?var=hello%5Bworld%5D | ?var=he |
& | &var=hello%5Bworld%5D | &var=hello%5Bworld%5D | &var=he |
Given {"var": ["one", "two", "three"]}
, the expression {var}
expands to one,two,three
.
The following table shows an output matrix for every possible operator/modifier combination produced for array
input.
Modifier | |||
---|---|---|---|
Operator | None | * | :2 |
None | one,two,three | one,two,three | on,tw,th |
+ | one,two,three | one,two,three | on,tw,th |
# | #one,two,three | #one,two,three | #on,tw,th |
. | .one,two,three | .one.two.three | .on,tw,th |
/ | /one,two,three | /one/two/three | /on,tw,th |
; | ;var=one,two,three | ;var=one;var=two;var=three | ;var=on,tw,th |
? | ?var=one,two,three | ?var=one&var=two&var=three | ?var=on,tw,th |
& | &var=one,two,three | &var=one&var=two&var=three | &var=on,tw,th |
Given {"var": {"one": "alpha", "two": "bravo"}}
, the expression {var}
expands to one,two,three
.
The following table shows an output matrix for every possible operator/modifier combination produced for object
input.
Modifier | |||
---|---|---|---|
Operator | None | * | :2 |
None | one,alpha,two,bravo | one=alpha,two=bravo | on,al,tw,br |
+ | one,alpha,two,bravo | one=alpha,two=bravo | on,al,tw,br |
# | #one,alpha,two,bravo | #one=alpha,two=bravo | #on,al,tw,br |
. | .one,alpha,two,bravo | .one=alpha.two=bravo | .on,al,tw,br |
/ | /one,alpha,two,bravo | /one=alpha/two=bravo | /on,al,tw,br |
; | ;var=one,alpha,two,bravo | ;one=alpha;two=bravo | ;var=on,al,tw,br |
? | ?var=one,alpha,two,bravo | ?one=alpha&two=bravo | ?var=on,al,tw,br |
& | &var=one,alpha,two,bravo | &one=alpha&two=bravo | &var=on,al,tw,br |
URI Template is a Proposed Standard and because of that I did not want to deviate from it. That said I'm not at all happy with how the specification turned out. Here are some of my thoughts:
{?some_object}
should lead to ?foo=bar&hello=world
, as this is the common expansionabc
may become a/bc
or a/ab/abc
.
But there is no way of modifying output to a/b/c
or a/b/abc
. Whenever I had to partition identifier spaces, I used one of the latter patterns..
automatically prefix the expansion. So {"var": ["filename", "extension"]}
and {.var*}
results in .filename.extension
- obviously not what I wanted.ALPHA / DIGIT / "_" / pct-encoded
and may not be decoded for resolving the reference. This simply feels weird, especially the "may not be decoded" part.{/var,empty,empty}
results in /foobar//
- clearly not what one intended{var}
and {"var" : {"a": "1", "b": "2"}}
results in a,1,b,2
- excusemewhat? I would've expected a=1,b=2
or a:1,b:2
(in a perverse parallel universe).+
, not %20
according to application/x-www-form-urlencoded