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";
}});
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