= Vert.x URI Template
:toc: left

An implementation of the https://datatracker.ietf.org/doc/html/rfc6570[URI Template RFC].

This implements the spec level 4.

WARNING: This module has Tech Preview status, this means the API can change between versions.

== Using the URI Template

To use this component, add the following dependency to the dependencies section of your build descriptor:

* Maven (in your `pom.xml`):

[source,xml,subs="+attributes"]
----
<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-uri-template</artifactId>
  <version>${maven.version}</version>
</dependency>
----

* Gradle (in your `build.gradle` file):

[source,groovy,subs="+attributes"]
----
compile io.vertx:vertx-uri-template:${maven.version}
----

== Templates

You can create a template very easily

[source,$lang]
----
{@link examples.UriTemplateExamples#exampleCreate}
----

Only a valid template can be created, any invalid template is rejected with an exception at creation time:

[source,$lang]
----
{@link examples.UriTemplateExamples#exampleInvalid}
----

== Template expansion

You can call {@link io.vertx.uritemplate.UriTemplate#expandToString} with a list of variables to produce a valid URI string

[source,$lang]
----
{@link examples.UriTemplateExamples#exampleExpansion}
----

{@link io.vertx.uritemplate.Variables} holds the variables used by template expansion

* you can set a single valued variable

[source,$lang]
----
{@link examples.UriTemplateExamples#exampleSingleValueVariable}
----

* you can set a list valued variable

[source,$lang]
----
{@link examples.UriTemplateExamples#exampleListVariable}
----

* you can set a map valued variable

[source,$lang]
----
{@link examples.UriTemplateExamples#exampleMapVariable}
----

As seen earlier, variables are referenced within a template by their name surrounded with curly braces, e.g `{host}` references the `host` variable, this
expansion is known as _simple string expansion_.

NOTE: There are other <<expansion_styles,expansion styles>>, they differ from simple string expansion by their formatting and encoding.

Lists are expanded to a comma separated value

[source,java]
----
UriTemplate template = UriTemplate.of("http://server.com/products?sort={sort}");
Variables variables = Variables.variables().set("sort", Arrays.asList("price", "name"))
assertEquals("http://server.com/products?sort=price,name", template.expandToString(variables));
----

Likewise, map values are expanded to comma separated values, e.g `{ "firstName": "Dale", "lastName": "Cooper" }` expands to `"firstName,Dale,lastName,Cooper"`
, unless they are _exploded_ with an _asterisk_ modifier

[source,java]
----
UriTemplate template = UriTemplate.of("http://server.com/?details={details*}");
Map<String, String> details = new HashMap<>();
details.put("firstName", "Dale");
details.put("lastName", "Cooper");
Variables variables = Variables.variables().set("details", details)
assertEquals("http://server.com/?firstName=Dale,lastName=Cooper", template.expandToString(variables));
----

== Template encoding

Literal characters outside the <<reserved_set,reserved>> set and <<unreserved_set,unreserved>> set are https://en.wikipedia.org/wiki/Percent-encoding[percent-encoded]:

[source,java]
----
assertEquals("http://server.com/currency/%E2%82%AC", UriTemplate.of("http://server.com/currency/€").expandToString(variables));
----

Simple string expansion encodes any character outside the <<unreserved_set,unreserved>> set to its percent-encoded equivalent:

[source,java]
----
Variables variables = Variables.variables();
variables.set("currency", "€")
assertEquals("http://server.com/currency/%E2%82%AC", UriTemplate.of("http://server.com/currency/{currency}").expandToString(variables));
----

[[expansion_styles]]
== Expansion styles

Beyond simple string expansion, you can use other expansion styles, expansion follows the syntax `'{' operator? varname '}'`.

=== Simple string expansion

The default expansion style when no operator is given.

* Syntax: `{varname}`
* Allowed characters: <<unreserved_set,unreserved>> set

[stripes=even,frame=none,grid=cols,cols="3,1,3"]
|===
|Vars | Template | URI

|{ "who": "fred" } | {who} | fred
|{ "unreserved": "_" } | {unreserved} | _
|{ "reserved": "/" } | {reserved} | %2F
|{ "pct_encoded": "%2F" } | {pct_encoded} | %252F
|{ "x": "1024", "y" : "768" } | {x,y} | 1024/y768
|{ "list": [ "red", "green", "blue" ] } | {list} | red,green,blue
|{ "list": [ "red", "green", "blue" ] } | {list*} | red,green,blue
|{ "map": { "firstName": "Dale", "lastName": "Cooper" } } | {map} | firstName,Dale,lastName,Cooper
|{ "map": { "firstName": "Dale", "lastName": "Cooper" } } | {map*} | firstName=Dale,lastName=Cooper
|===

=== Path segment expansion

Path segment expansion is useful for creating hierarchical URI path.

* Syntax: `{/varname}`
* Allowed characters: <<unreserved_set,unreserved>> set

[stripes=even,frame=none,grid=cols,cols="3,1,3"]
|===
|Vars | Template | URI

|{ "who": "fred" } | {/who} | /fred
|{ "unreserved": "_" } | {/unreserved} | /_
|{ "reserved": "/" } | {/reserved} | /%2F
|{ "pct_encoded": "%2F" } | {/pct_encoded} | /%252F
|{ "x": "1024", "y" : "768" } | {/x,y} | /1024/y768
|{ "list": [ "red", "green", "blue" ] } | {/list} | /red,green,blue
|{ "list": [ "red", "green", "blue" ] } | {/list*} | /red/green/blue
|{ "map": { "firstName": "Dale", "lastName": "Cooper" } } | {/map} | /firstName,Dale,lastName,Cooper
|{ "map": { "firstName": "Dale", "lastName": "Cooper" } } | {/map*} | /firstName=Dale/lastName=Cooper
|===

=== Form-style query expansion

Form-style query expansion is useful for creating query strings.

* Syntax: `{?varname}`
* Allowed characters: <<unreserved_set,unreserved>> set

[stripes=even,frame=none,grid=cols,cols="3,1,3"]
|===
|Vars | Template | URI

|{ "who": "fred" } | {?who} | ?who=fred
|{ "unreserved": "_" } | {?unreserved} | ?unreserved=_
|{ "reserved": "/" } | {?reserved} | ?reserved=%2F
|{ "pct_encoded": "%2F" } | {?pct_encoded} | ?pct_encoded=%252F
|{ "x": "1024", "y" : "768" } | {?x,y} | ?x=1024&y768
|{ "list": [ "red", "green", "blue" ] } | {?list} | ?list=red,green,blue
|{ "list": [ "red", "green", "blue" ] } | {?list*} | ?list=red&list=green&list=blue
|{ "map": { "firstName": "Dale", "lastName": "Cooper" } } | {?map} | ?map=firstName,Dale,lastName,Cooper
|{ "map": { "firstName": "Dale", "lastName": "Cooper" } } | {?map*} | ?firstName=Dale&lastName=Cooper
|===

=== Form-style query continuation

Form-style query continuation is useful for appending to query strings.

* Syntax: `{&varname}`
* Allowed characters: <<unreserved_set,unreserved>> set

[stripes=even,frame=none,grid=cols,cols="3,1,3"]
|===
|Vars | Template | URI

|{ "who": "fred" } | {&who} | &who=fred
|{ "unreserved": "_" } | {&unreserved} | &unreserved=_
|{ "reserved": "/" } | {&reserved} | &reserved=%2F
|{ "pct_encoded": "%2F" } | {&pct_encoded} | &pct_encoded=%252F
|{ "x": "1024", "y" : "768" } | {&x,y} | &x=1024&y768
|{ "list": [ "red", "green", "blue" ] } | {&list} | &list=red,green,blue
|{ "list": [ "red", "green", "blue" ] } | {&list*} | &list=red&list=green&list=blue
|{ "map": { "firstName": "Dale", "lastName": "Cooper" } } | {&map} | &map=firstName,Dale,lastName,Cooper
|{ "map": { "firstName": "Dale", "lastName": "Cooper" } } | {&map*} | &firstName=Dale&lastName=Cooper
|===

=== Reserved expansion

Extends the allowed character set of the simple string expansion to the reserved set and pct-encoded sequences.

* Syntax: `{+varname}`
* Allowed characters: <<unreserved_set,unreserved>> set, <<reserved_set,unreserved>> set and pct-encoded sequence.

[stripes=even,frame=none,grid=cols,cols="3,1,3"]
|===
|Vars | Template | URI

|{ "who": "fred" } | {+who} | fred
|{ "unreserved": "_" } | {+unreserved} | _
|{ "reserved": "/" } | {+reserved} | /
|{ "pct_encoded": "%2F" } | {+pct_encoded} | %2F
|{ "x": "1024", "y" : "768" } | {+x,y} | 1024,y768
|{ "list": [ "red", "green", "blue" ] } | {+list} | red,green,blue
|{ "list": [ "red", "green", "blue" ] } | {+list*} | red,green,blue
|{ "map": { "firstName": "Dale", "lastName": "Cooper" } } | {+map} | firstName,Dale,lastName,Cooper
|{ "map": { "firstName": "Dale", "lastName": "Cooper" } } | {+map*} | firstName=Dale,lastName=Cooper
|===

=== Fragment expansion

Like the reserved expansion prefixed by `#`.

* Syntax: `{#varname}`
* Allowed characters: <<unreserved_set,unreserved>> set, <<reserved_set,unreserved>> set and pct-encoded

[stripes=even,frame=none,grid=cols,cols="3,1,3"]
|===
|Vars | Template | URI

|{ "who": "fred" } | {#who} | #fred
|{ "unreserved": "_" } | {#unreserved} | #_
|{ "reserved": "/" } | {#reserved} | #/
|{ "pct_encoded": "%2F" } | {#pct_encoded} | #%2F
|{ "x": "1024", "y" : "768" } | {#x,y} | #1024,y768
|{ "list": [ "red", "green", "blue" ] } | {#list} | #red,green,blue
|{ "list": [ "red", "green", "blue" ] } | {#list*} | #red,green,blue
|{ "map": { "firstName": "Dale", "lastName": "Cooper" } } | {#map} | #firstName,Dale,lastName,Cooper
|{ "map": { "firstName": "Dale", "lastName": "Cooper" } } | {#map*} | #firstName=Dale,lastName=Cooper
|===

=== Label expansion with Dot-Prefix

* Syntax: `{.varname}`
* Allowed characters: <<unreserved_set,unreserved>> set

[stripes=even,frame=none,grid=cols,cols="3,1,3"]
|===
|Vars | Template | URI

|{ "who": "fred" } | {.who} | .fred
|{ "unreserved": "_" } | {.unreserved} | ._
|{ "reserved": "/" } | {.reserved} | .%2F
|{ "pct_encoded": "%2F" } | {.pct_encoded} | .%252F
|{ "x": "1024", "y" : "768" } | {.x,y} | .1024.y768
|{ "list": [ "red", "green", "blue" ] } | {.list} | .red,green,blue
|{ "list": [ "red", "green", "blue" ] } | {.list*} | .red.green.blue
|{ "map": { "firstName": "Dale", "lastName": "Cooper" } } | {.map} | .firstName,Dale,lastName,Cooper
|{ "map": { "firstName": "Dale", "lastName": "Cooper" } } | {.map*} | .firstName=Dale.lastName=Cooper
|===

=== Path-Style Parameter Expansion

* Syntax: `{.varname}`
* Allowed characters: <<unreserved_set,unreserved>> set

[stripes=even,frame=none,grid=cols,cols="3,1,3"]
|===
|Vars | Template | URI

|{ "who": "fred" } | {;who} | ;who=fred
|{ "unreserved": "_" } | {;unreserved} | ;unreserved=_
|{ "reserved": "/" } | {;reserved} | ;reserved=%2F
|{ "pct_encoded": "%2F" } | {;pct_encoded} | ;pct_encoded=%252F
|{ "x": "1024", "y" : "768" } | {;x,y} | ;x=1024;y=y768
|{ "list": [ "red", "green", "blue" ] } | {;list} | ;list=red,green,blue
|{ "list": [ "red", "green", "blue" ] } | {;list*} | ;list=red;list=green;list=blue
|{ "map": { "firstName": "Dale", "lastName": "Cooper" } } | {;map} | ;map=firstName,Dale,lastName,Cooper
|{ "map": { "firstName": "Dale", "lastName": "Cooper" } } | {;map*} | ;firstName=Dale;lastName=Cooper
|===

== Characters set cheatsheet

=== Reserved set

[[reserved_set]]
`!*'();:@&=+$,/?#[]`

=== Unreserved set

[[unreserved_set]]
`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~`
