RFC 6570

URI Template

๐Ÿ“… published March 2012, from ietf

URI Template ๐Ÿฆ‹

Part 1 of 7

okay bestie, this is RFC 6570, the spec for URI Templates ๐Ÿ’– It's an Internet Standards Track document from the Internet Engineering Task Force (IETF), and it was published in March 2012 with ISSN 2070-1721. The authors are J. Gregorio (Google), R. Fielding (Adobe), M. Hadley (MITRE), M. Nottingham (Rackspace), and D. Orchard (Salesforce.com). The header doesn't list any RFCs that it obsoletes or updates, so there's no drama there ๐ŸŒธ

Abstract โœจ

okay so a URI Template is a compact string of characters that describes a whole range of Uniform Resource Identifiers using variable expansion ๐ŸŽ€ This spec defines the URI Template syntax, the process for expanding a URI Template into a URI reference, and guidelines for using URI Templates on the Internet ๐Ÿซถ

Status of This Memo ๐Ÿ“œ

this is an Internet Standards Track document ๐Ÿ”ฅ

it's a product of the Internet Engineering Task Force (IETF) and represents the consensus of the IETF community. it got public review and was approved for publication by the Internet Engineering Steering Group (IESG). if you want more on Internet Standards, check Section 2 of RFC 5741 ๐Ÿ’…

for the current status, any errata, and how to give feedback on it, go to http://www.rfc-editor.org/info/rfc6570 ๐Ÿงš

Copyright (c) 2012 IETF Trust and the persons identified as the document authors. All rights reserved.

this document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info) as they stand on the date of publication. please read these carefully because they spell out your rights and restrictions for this document ๐Ÿ‘€ Code Components pulled out of this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions, and they come without warranty, as described in the Simplified BSD License ๐Ÿ’œ

Table of Contents ๐Ÿ“

   1. Introduction ....................................................3
      1.1. Overview ...................................................3
      1.2. Levels and Expression Types ................................5
      1.3. Design Considerations ......................................9
      1.4. Limitations ...............................................10
      1.5. Notational Conventions ....................................11
      1.6. Character Encoding and Unicode Normalization ..............12
   2. Syntax .........................................................13
      2.1. Literals ..................................................13
      2.2. Expressions ...............................................13
      2.3. Variables .................................................14
      2.4. Value Modifiers ...........................................15
           2.4.1. Prefix Values ......................................15
           2.4.2. Composite Values ...................................16
   3. Expansion ......................................................18
      3.1. Literal Expansion .........................................18
      3.2. Expression Expansion ......................................18
           3.2.1. Variable Expansion .................................19
           3.2.2. Simple String Expansion: {var} .....................21
           3.2.3. Reserved Expansion: {+var} .........................22
           3.2.4. Fragment Expansion: {#var} .........................23
           3.2.5. Label Expansion with Dot-Prefix: {.var} ............24
           3.2.6. Path Segment Expansion: {/var} .....................24
           3.2.7. Path-Style Parameter Expansion: {;var} .............25
           3.2.8. Form-Style Query Expansion: {?var} .................26
           3.2.9. Form-Style Query Continuation: {&var} ..............27
   4. Security Considerations ........................................27
   5. Acknowledgments ................................................28
   6. References .....................................................28
      6.1. Normative References ......................................28
      6.2. Informative References ....................................29
   Appendix A. Implementation Hints ..................................30

1. Introduction โœจ

1.1. Overview ๐ŸŒธ

okay so a Uniform Resource Identifier (URI) [RFC3986] is often used to identify one specific resource inside a common space of similar resources (informally, a "URI space") ๐Ÿ’– For example, personal web spaces are often set up with a common pattern, like this:

     http://example.com/~fred/
     http://example.com/~mark/

or a set of dictionary entries might be grouped in a hierarchy by the first letter of the term, as in:

     http://example.com/dictionary/c/cat
     http://example.com/dictionary/d/dog

or a service interface might get invoked with various user input in a common pattern, as in:

     http://example.com/search?q=cat&lang=en
     http://example.com/search?q=chien&lang=fr

A URI Template is a compact sequence of characters for describing a range of Uniform Resource Identifiers through variable expansion ๐Ÿฆ‹

URI Templates give you a mechanism for abstracting a space of resource identifiers, so the variable parts are easy to spot and describe ๐Ÿ‘‘ They can do a lot: discovering available services, configuring resource mappings, defining computed links, specifying interfaces, and other kinds of programmatic interaction with resources. For example, the resources above could be described by these URI Templates:

     http://example.com/~{username}/
     http://example.com/dictionary/{term:1}/{term}
     http://example.com/search{?q,lang}

we define the following terms (keep these in your back pocket ๐Ÿง ):

  • expression: The text between '{' and '}', including the enclosing braces, as defined in Section 2.
  • expansion: The string result obtained from a template expression after processing it according to its expression type, list of variable names, and value modifiers, as defined in Section 3.
  • template processor: A program or library that, given a URI Template and a set of variables with values, transforms the template string into a URI reference by parsing the template for expressions and substituting each one with its corresponding expansion.

a URI Template gives you both a structural description of a URI space and, once you provide variable values, machine-readable instructions for building a URI that matches those values ๐ŸŽ€ A URI Template becomes a URI reference when each delimited expression gets replaced with its value, based on the expression type and the values of the variables named inside it. Expression types run from simple string expansion all the way up to multiple name=value lists. The expansions are based on the URI generic syntax, so an implementation can process any URI Template without knowing the scheme-specific requirements of every possible resulting URI. iconic, right? ๐Ÿ˜ญ

for example, this URI Template has a form-style parameter expression, which you can spot by the "?" operator sitting in front of the variable names:

     http://www.example.com/foo{?query,number}

the expansion process for expressions that start with the question-mark ("?") operator follows the same pattern as form-style interfaces on the World Wide Web ๐ŸŒˆ

     http://www.example.com/foo{?query,number}
                               \_____________/
                                  |
                                  |
             For each defined variable in [ 'query', 'number' ],
             substitute "?" if it is the first substitution or "&"
             thereafter, followed by the variable name, '=', and the
             variable's value.

if the variables have these values:

     query  := "mycelium"
     number := 100

then the expansion of the URI Template above is:

     http://www.example.com/foo?query=mycelium&number=100

if 'query' is undefined instead, the expansion would be:

     http://www.example.com/foo?number=100

and if both variables are undefined, it would be:

     http://www.example.com/foo

a URI Template can be provided in absolute form (like the examples above) or in relative form ๐Ÿงš A template gets expanded before the resulting reference is resolved from relative to absolute form.

even though the URI syntax is used for the result, the template string can contain the broader set of characters found in Internationalized Resource Identifier (IRI) references [RFC3987] ๐Ÿ’… So a URI Template is also an IRI template, and the result of template processing can be turned into an IRI by following the process defined in Section 3.2 of [RFC3987] โœจ

1.2. Levels and Expression Types ๐Ÿ’œ

URI Templates are kind of like a macro language with a fixed set of macro definitions, and the expression type decides how the expansion works ๐Ÿ“ The default expression type is simple string expansion. A single named variable gets replaced by its value as a string, after pct-encoding any characters that aren't in the set of unreserved URI characters (Section 1.5).

most template processors built before this spec only implemented that default expression type, so we call those Level 1 templates ๐ŸŒธ Here's the Level 1 cheat sheet:

   .-----------------------------------------------------------------.
   | Level 1 examples, with variables having values of               |
   |                                                                 |
   |             var   := "value"                                    |
   |             hello := "Hello World!"                             |
   |                                                                 |
   |-----------------------------------------------------------------|
   | Op       Expression            Expansion                        |
   |-----------------------------------------------------------------|
   |     | Simple string expansion                       (Sec 3.2.2) |
   |     |                                                           |
   |     |    {var}                 value                            |
   |     |    {hello}               Hello%20World%21                 |
   `-----------------------------------------------------------------'

Level 2 templates add the plus ("+") operator, for expanding values that are allowed to include reserved URI characters (Section 1.5), and the crosshatch ("#") operator for expanding fragment identifiers ๐Ÿฆ‹

Okay so Level 2 is where the templates start getting spicy ๐ŸŒธ Here are the examples, with the variables set up first:

   .-----------------------------------------------------------------.
   | Level 2 examples, with variables having values of               |
   |                                                                 |
   |             var   := "value"                                    |
   |             hello := "Hello World!"                             |
   |             path  := "/foo/bar"                                 |
   |                                                                 |
   |-----------------------------------------------------------------|
   | Op       Expression            Expansion                        |
   |-----------------------------------------------------------------|
   |  +  | Reserved string expansion                     (Sec 3.2.3) |
   |     |                                                           |
   |     |    {+var}                value                            |
   |     |    {+hello}              Hello%20World!                   |
   |     |    {+path}/here          /foo/bar/here                    |
   |     |    here?ref={+path}      here?ref=/foo/bar                |
   |-----+-----------------------------------------------------------|
   |  #  | Fragment expansion, crosshatch-prefixed       (Sec 3.2.4) |
   |     |                                                           |
   |     |    X{#var}               X#value                          |
   |     |    X{#hello}             X#Hello%20World!                 |
   `-----------------------------------------------------------------'

Level 3 is the glow-up ๐Ÿ’– Templates can now stuff multiple variables into one expression, separated by commas. They also get some fancier operators: dot-prefixed labels, slash-prefixed path segments, semicolon-prefixed path parameters, and form-style query strings made of name=value pairs joined with an ampersand (&). Literally so iconic ๐Ÿ˜ญ

Here's the Level 3 example box:

   .-----------------------------------------------------------------.
   | Level 3 examples, with variables having values of               |
   |                                                                 |
   |             var   := "value"                                    |
   |             hello := "Hello World!"                             |
   |             empty := ""                                         |
   |             path  := "/foo/bar"                                 |
   |             x     := "1024"                                     |
   |             y     := "768"                                      |
   |                                                                 |
   |-----------------------------------------------------------------|
   | Op       Expression            Expansion                        |
   |-----------------------------------------------------------------|
   |     | String expansion with multiple variables      (Sec 3.2.2) |
   |     |                                                           |
   |     |    map?{x,y}             map?1024,768                     |
   |     |    {x,hello,y}           1024,Hello%20World%21,768        |
   |     |                                                           |
   |-----+-----------------------------------------------------------|
   |  +  | Reserved expansion with multiple variables    (Sec 3.2.3) |
   |     |                                                           |
   |     |    {+x,hello,y}          1024,Hello%20World!,768          |
   |     |    {+path,x}/here        /foo/bar,1024/here               |
   |     |                                                           |
   |-----+-----------------------------------------------------------|
   |  #  | Fragment expansion with multiple variables    (Sec 3.2.4) |
   |     |                                                           |
   |     |    {#x,hello,y}          #1024,Hello%20World!,768         |
   |     |    {#path,x}/here        #/foo/bar,1024/here              |
   |     |                                                           |
   |-----+-----------------------------------------------------------|
   |  .  | Label expansion, dot-prefixed                 (Sec 3.2.5) |
   |     |                                                           |
   |     |    X{.var}               X.value                          |
   |     |    X{.x,y}               X.1024.768                       |
   |     |                                                           |
   |-----+-----------------------------------------------------------|
   |  /  | Path segments, slash-prefixed                 (Sec 3.2.6) |
   |     |                                                           |
   |     |    {/var}                /value                           |
   |     |    {/var,x}/here         /value/1024/here                 |
   |     |                                                           |
   |-----+-----------------------------------------------------------|
   |  ;  | Path-style parameters, semicolon-prefixed     (Sec 3.2.7) |
   |     |                                                           |
   |     |    {;x,y}                ;x=1024;y=768                    |
   |     |    {;x,y,empty}          ;x=1024;y=768;empty              |
   |     |                                                           |
   |-----+-----------------------------------------------------------|
   |  ?  | Form-style query, ampersand-separated         (Sec 3.2.8) |
   |     |                                                           |
   |     |    {?x,y}                ?x=1024&y=768                    |
   |     |    {?x,y,empty}          ?x=1024&y=768&empty=             |
   |     |                                                           |
   |-----+-----------------------------------------------------------|
   |  &  | Form-style query continuation                 (Sec 3.2.9) |
   |     |                                                           |
   |     |    ?fixed=yes{&x}        ?fixed=yes&x=1024                |
   |     |    {&x,y,empty}          &x=1024&y=768&empty=             |
   |     |                                                           |
   `-----------------------------------------------------------------'

And last but definitely not least, Level 4 ๐Ÿฆ‹ It adds value modifiers as an optional suffix on each variable name. A prefix modifier (:) means only a limited number of characters from the start of the value get used by the expansion (Section 2.4.1). An explode (*) modifier means the variable gets treated as a composite value, either a list of names or an associative array of (name, value) pairs, and it's expanded as if each member were its own separate variable (Section 2.4.2). Main character energy ๐Ÿ‘‘

   .-----------------------------------------------------------------.
   | Level 4 examples, with variables having values of               |
   |                                                                 |
   |             var   := "value"                                    |
   |             hello := "Hello World!"                             |
   |             path  := "/foo/bar"                                 |
   |             list  := ("red", "green", "blue")                   |
   |             keys  := [("semi",";"),("dot","."),("comma",",")]   |
   |                                                                 |
   | Op       Expression            Expansion                        |
   |-----------------------------------------------------------------|
   |     | String expansion with value modifiers         (Sec 3.2.2) |
   |     |                                                           |
   |     |    {var:3}               val                              |
   |     |    {var:30}              value                            |
   |     |    {list}                red,green,blue                   |
   |     |    {list*}               red,green,blue                   |
   |     |    {keys}                semi,%3B,dot,.,comma,%2C         |
   |     |    {keys*}               semi=%3B,dot=.,comma=%2C         |
   |     |                                                           |
   |-----+-----------------------------------------------------------|
   |  +  | Reserved expansion with value modifiers       (Sec 3.2.3) |
   |     |                                                           |
   |     |    {+path:6}/here        /foo/b/here                      |
   |     |    {+list}               red,green,blue                   |
   |     |    {+list*}              red,green,blue                   |
   |     |    {+keys}               semi,;,dot,.,comma,,             |
   |     |    {+keys*}              semi=;,dot=.,comma=,             |
   |     |                                                           |
   |-----+-----------------------------------------------------------|
   |  #  | Fragment expansion with value modifiers       (Sec 3.2.4) |
   |     |                                                           |
   |     |    {#path:6}/here        #/foo/b/here                     |
   |     |    {#list}               #red,green,blue                  |
   |     |    {#list*}              #red,green,blue                  |
   |     |    {#keys}               #semi,;,dot,.,comma,,            |
   |     |    {#keys*}              #semi=;,dot=.,comma=,            |
   |     |                                                           |
   |-----+-----------------------------------------------------------|
   |  .  | Label expansion, dot-prefixed                 (Sec 3.2.5) |
   |     |                                                           |
   |     |    X{.var:3}             X.val                            |
   |     |    X{.list}              X.red,green,blue                 |
   |     |    X{.list*}             X.red.green.blue                 |
   |     |    X{.keys}              X.semi,%3B,dot,.,comma,%2C       |
   |     |    X{.keys*}             X.semi=%3B.dot=..comma=%2C       |
   |     |                                                           |
   |-----+-----------------------------------------------------------|
   |  /  | Path segments, slash-prefixed                 (Sec 3.2.6) |
   |     |                                                           |
   |     |    {/var:1,var}          /v/value                         |
   |     |    {/list}               /red,green,blue                  |
   |     |    {/list*}              /red/green/blue                  |
   |     |    {/list*,path:4}       /red/green/blue/%2Ffoo           |
   |     |    {/keys}               /semi,%3B,dot,.,comma,%2C        |
   |     |    {/keys*}              /semi=%3B/dot=./comma=%2C        |
   |     |                                                           |
   |-----+-----------------------------------------------------------|
   |  ;  | Path-style parameters, semicolon-prefixed     (Sec 3.2.7) |
   |     |                                                           |
   |     |    {;hello:5}            ;hello=Hello                     |
   |     |    {;list}               ;list=red,green,blue             |
   |     |    {;list*}              ;list=red;list=green;list=blue   |
   |     |    {;keys}               ;keys=semi,%3B,dot,.,comma,%2C   |
   |     |    {;keys*}              ;semi=%3B;dot=.;comma=%2C        |
   |     |                                                           |
   |-----+-----------------------------------------------------------|
   |  ?  | Form-style query, ampersand-separated         (Sec 3.2.8) |
   |     |                                                           |
   |     |    {?var:3}              ?var=val                         |
   |     |    {?list}               ?list=red,green,blue             |
   |     |    {?list*}              ?list=red&list=green&list=blue   |
   |     |    {?keys}               ?keys=semi,%3B,dot,.,comma,%2C   |
   |     |    {?keys*}              ?semi=%3B&dot=.&comma=%2C        |
   |     |                                                           |
   |-----+-----------------------------------------------------------|
   |  &  | Form-style query continuation                 (Sec 3.2.9) |
   |     |                                                           |
   |     |    {&var:3}              &var=val                         |
   |     |    {&list}               &list=red,green,blue             |
   |     |    {&list*}              &list=red&list=green&list=blue   |
   |     |    {&keys}               &keys=semi,%3B,dot,.,comma,%2C   |
   |     |    {&keys*}              &semi=%3B&dot=.&comma=%2C        |
   |     |                                                           |
   `-----------------------------------------------------------------'

1.3. Design Considerations ๐ŸŽ€

okay so bestie, mechanisms that work like URI Templates already exist in a few specs, including WSDL [WSDL], WADL [WADL], and OpenSearch [OpenSearch]. This spec extends them and formally defines the syntax, so URI Templates can be used consistently across lots of Internet apps and inside Internet message fields, while still staying compatible with those earlier definitions. โœจ

The syntax is giving balance ๐Ÿ’…. It's powerful enough for a serious expansion mechanism but still easy to implement. It's designed to be trivial to parse while still flexible enough to express lots of common template scenarios, and implementations can parse the template and do the expansions in a single pass. ๐Ÿงš

Templates are simple and readable in common examples because the single-character operators match the URI generic syntax delimiters. The operator's delimiter (".", ";", "/", "?", "&", and "#") gets left out when none of the listed variables are defined. For ";" (path-style parameters), the "=" gets omitted when the variable value is empty, but for "?" (form-style parameters) the "=" stays even when the value is empty. Multiple variables and list values get joined with "," unless the operator has its own joining mechanism. The "+" and "#" operators substitute unencoded reserved characters found inside variable values, while the other operators pct-encode reserved characters in variable values before expansion. ๐ŸŒธ

Level 1 template expressions cover the most common URI space cases. If we only cared about URI generation, the syntax could be limited to simple variable expansion, since fancier forms could be made by changing the variable values. But URI Templates also aim to describe the layout of identifiers in terms of data values that already exist, so the syntax includes operators that reflect how resource identifiers are commonly allocated. Prefix substrings are often used to split up huge spaces of resources, so modifiers on variable values let you name both the substring and the full value string with a single variable name. ๐ŸŒˆ

1.4. Limitations ๐Ÿฆ‹

Since a URI Template describes a superset of the identifiers, there is no implication that every possible expansion of each delimited variable expression corresponds to a URI of an existing resource. The expectation is that an application constructing URIs from the template will be given an appropriate set of values for the variables being substituted, or at least a way of validating user data entry for those values. ๐Ÿ™ˆ

URI Templates are not URIs. They do not identify an abstract or physical resource, they are not parsed as URIs, and they should not be used in places where a URI would be expected unless the template expressions will be expanded by a template processor prior to use. Distinct field, element, or attribute names should be used to tell apart protocol elements that carry a URI Template from those that expect a URI reference. > Keep these two things straight, bestie, or you'll get very confused very fast ๐Ÿ’€

Some URI Templates can be used in reverse for variable matching, which means comparing the template to a fully formed URI, pulling out the variable parts, and assigning them to the named variables. Variable matching only works well if the template expressions are delimited by the beginning or end of the URI, or by characters that cannot be part of the expansion, such as reserved characters surrounding a simple string expression. In general, regular expression languages are better suited for variable matching. ๐Ÿ“

1.5. Notational Conventions ๐Ÿ“–

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119].

This specification uses the Augmented Backus-Naur Form (ABNF) notation of [RFC5234]. The following ABNF rules are imported from the normative references [RFC5234], [RFC3986], and [RFC3987].

     ALPHA          =  %x41-5A / %x61-7A   ; A-Z / a-z
     DIGIT          =  %x30-39             ; 0-9
     HEXDIG         =  DIGIT / "A" / "B" / "C" / "D" / "E" / "F"
                       ; case-insensitive

     pct-encoded    =  "%" HEXDIG HEXDIG
     unreserved     =  ALPHA / DIGIT / "-" / "." / "_" / "~"
     reserved       =  gen-delims / sub-delims
     gen-delims     =  ":" / "/" / "?" / "#" / "[" / "]" / "@"
     sub-delims     =  "!" / "$" / "&" / "'" / "(" / ")"
                    /  "*" / "+" / "," / ";" / "="

     ucschar        =  %xA0-D7FF / %xF900-FDCF / %xFDF0-FFEF
                    /  %x10000-1FFFD / %x20000-2FFFD / %x30000-3FFFD
                    /  %x40000-4FFFD / %x50000-5FFFD / %x60000-6FFFD
                    /  %x70000-7FFFD / %x80000-8FFFD / %x90000-9FFFD
                    /  %xA0000-AFFFD / %xB0000-BFFFD / %xC0000-CFFFD
                    /  %xD0000-DFFFD / %xE1000-EFFFD

     iprivate       =  %xE000-F8FF / %xF0000-FFFFD / %x100000-10FFFD

1.6. Character Encoding and Unicode Normalization โœจ

This spec borrows a bunch of terms from [RFC6365], including "character", "character encoding scheme", "code point", "coded character set", "glyph", "non-ASCII", "normalization", "protocol element", and "regular expression". Same meanings, no sneaky new definitions ๐Ÿค“

Okay so ABNF says its terminal values are non-negative integers (code points) that are a superset of the US-ASCII coded character set [ASCII]. This spec goes one step further and defines terminal values as code points in the Unicode coded character set [UNIV6] ๐ŸŒˆ

Here's the vibe check: even though the syntax and expansion stuff is written in terms of Unicode code points, templates show up in real life as characters in whatever form or encoding fits the situation. That could be octets inside a network message, or glyphs painted on the side of a bus ๐ŸšŒ This spec doesn't force any one character encoding scheme for turning URI Template characters into the octets used to store or send them. If a URI Template shows up inside a protocol element, that protocol decides the encoding. If it doesn't say, the template is assumed to use the same encoding as the text around it ๐ŸŽ€ The only time a string of template characters is REQUIRED to be handled as a sequence of Unicode code points is during the template expansion process itself ๐Ÿ’–

Next up, normalization, which is lowkey a whole thing ๐Ÿ’… The Unicode Standard [UNIV6] defines a bunch of ways that different character sequences count as equivalent. Unicode Standard Annex #15 [UTR15] lays out the Normalization Forms for those equivalences. A normalization form decides how you consistently encode equivalent strings. In theory, every URI processing implementation, template processors included, should use the same normalization form when generating a URI reference. In practice? Nope, they don't ๐Ÿ™ˆ If a value came from the same server as the resource, you can assume the string is already in the form that server expects ๐Ÿงš If a user typed the value in, like in a data-entry dialog, then the string SHOULD be normalized to Normalization Form C (NFC: Canonical Decomposition, followed by Canonical Composition) before a template processor uses it in an expansion ๐Ÿ“

Also, when non-ASCII data that represents readable strings gets pct-encoded for a URI reference, a template processor MUST first encode the string as UTF-8 [RFC3629] and then pct-encode any octets that aren't allowed in a URI reference. Non-negotiable, bestie ๐Ÿ’œ


2. Syntax ๐Ÿฆ‹

A URI Template is a string of printable Unicode characters that has zero or more embedded variable expressions. Each expression is wrapped in a matching pair of braces ({ and }) ๐ŸŒธ

URI-Template  = *( literals / expression )

Templates (and the processors that handle them) were described above in terms of four gradual levels, but the URI-Template syntax is defined using the ABNF for Level 4 ๐Ÿ‘‘ A template processor that's limited to lower-level templates MAY leave out the ABNF rules that only apply to higher levels. That said, it is RECOMMENDED that all parsers implement the full syntax, so unsupported levels can be properly identified as such to the end user โœจ

2.1. Literals ๐ŸŽ€

The characters outside of expressions in a URI Template string are meant to be copied literally into the URI reference, as long as the character is allowed in a URI (reserved / unreserved / pct-encoded). If it isn't allowed, it gets copied in as the sequence of pct-encoded triplets for that character's UTF-8 encoding [RFC3629] instead ๐Ÿ’…

literals      =  %x21 / %x23-24 / %x26 / %x28-3B / %x3D / %x3F-5B
              /  %x5D / %x5F / %x61-7A / %x7E / ucschar / iprivate
              /  pct-encoded
                   ; any Unicode character except: CTL, SP,
                   ;  DQUOTE, "'", "%" (aside from pct-encoded),
                   ;  "<", ">", "\", "^", "`", "{", "|", "}"

2.2. Expressions ๐ŸŒˆ

Template expressions are the parameterized bits of a URI Template ๐Ÿง Each expression has an optional operator, which sets the expression type and its expansion process. After that comes a comma-separated list of variable specifiers (variable names plus optional value modifiers). If there's no operator, the expression defaults to simple variable expansion of unreserved values ๐Ÿฆ‹

expression    =  "{" [ operator ] variable-list "}"
operator      =  op-level2 / op-level3 / op-reserve
op-level2     =  "+" / "#"
op-level3     =  "." / "/" / ";" / "?" / "&"
op-reserve    =  "=" / "," / "!" / "@" / "|"

The operator characters were picked to match their roles as reserved characters in the URI generic syntax, which is a very cute design choice ๐Ÿงš The operators defined in Section 3 of this spec are:

  • + 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 made of name=value pairs separated by "&" ๐Ÿ’œ
  • & Continuation of query-style &name=value pairs within a literal query component ๐Ÿ”ฅ

The operator characters equals (=), comma (,), exclamation (!), at sign (@), and pipe (|) are reserved for future extensions. Hands off for now, babe ๐Ÿ™…โ€โ™€๏ธ

The expression syntax specifically leaves out the dollar ($) and parentheses (( and )) characters, so they stay free for use outside this spec's scope ๐Ÿ’– For example, a macro language might use those characters to apply macro substitution to a string before that string gets processed as a URI Template โœจ

2.3. Variables ๐Ÿ‘‘

After the operator (if there is one), each expression holds a list of one or more comma-separated variable specifiers (varspec). Variable names have several jobs: they document what kinds of values are expected, they identify values for the template processor, and they're the literal string used as the name in name=value expansions (except when exploding an associative array). Variable names are case-sensitive, because the name might get expanded inside a case-sensitive URI component ๐Ÿง

variable-list =  varspec *( "," varspec )
varspec       =  varname [ modifier-level4 ]
varname       =  varchar *( ["."] varchar )
varchar       =  ALPHA / DIGIT / "_" / pct-encoded

A varname MAY contain one or more pct-encoded triplets. These triplets are considered an essential part of the variable name, so they are not decoded during processing. A varname with pct-encoded characters is not the same variable as a varname with those characters decoded. Applications that provide URI Templates are expected to be consistent about how they use pct-encoding in variable names ๐Ÿ™Œ

An expression MAY reference variables that are unknown to the template processor, or variables whose value is set to a special "undefined" value, like undef or null. Undefined variables get special treatment from the expansion process (Section 3.2.1) ๐ŸŒธ

A variable value that's a string of length zero is NOT undefined. It has the defined value of an empty string, so don't mix those up ๐Ÿซถ

In Level 4 templates, a variable can have a composite value, either a list of values or an associative array of (name, value) pairs. The template syntax doesn't indicate these value types directly, but they do affect the expansion process (Section 3.2.1) ๐Ÿงš

A variable defined as a list value is considered undefined if the list has zero members. A variable defined as an associative array of (name, value) pairs is considered undefined if the array has zero members, or if every member name in the array is associated with an undefined value ๐Ÿ’€

2.4. Value Modifiers ๐Ÿ“

Each variable in a Level 4 template expression can have a modifier. It either limits the expansion to a prefix of the variable's value string, or it explodes the expansion as a composite value, meaning a value list or an associative array of (name, value) pairs ๐ŸŽ€

modifier-level4 =  prefix / explode

2.4.1. Prefix Values ๐ŸŒˆ

A prefix modifier means the variable expansion is limited to a prefix of the variable's value string. Prefix modifiers are often used to partition an identifier space hierarchically, which is common in reference indices and hash-based storage. They also cap the expanded value at a maximum number of characters ๐Ÿ’–

prefix        =  ":" max-length
max-length    =  %x31-39 0*3DIGIT   ; positive integer < 10000

The max-length is a positive integer that refers to a maximum number of characters from the beginning of the variable's value, treated as a Unicode string. The count is in characters, not octets, so the expansion never splits a multi-octet-encoded character or lands in the middle of a pct-encoded triplet ๐Ÿงฉ If max-length is greater than the length of the variable's value, the entire value string is used โœจ

For example,

Given the variable assignments

  var   := "value"
  semi  := ";"

Example Template     Expansion

  {var}              value
  {var:20}           value
  {var:3}            val
  {semi}             %3B
  {semi:2}           %3B

2.4.2. Composite Values โœจ

Okay bestie, the explode (*) modifier tells the processor to treat a variable as a composite value, which is either a list of values or an associative array of (name, value) pairs ๐ŸŽ€ Each member then gets expanded as if it were its own separate variable. Lowkey, this is way less self-documenting than a non-exploded variable, because the variable name doesn't line up as clearly with how the URI reference looks after expansion ๐Ÿ™ˆ

     explode       =  "*"

URI Templates don't say what type or schema a variable is, so the type of an exploded variable gets decided by context ๐Ÿงš The processor might be handed values that mark things as strings, lists, or associative arrays. And wherever the template is used (a script, a markup language, an Interface Definition Language, etc.), there might be rules that match variable names to types, structures, or schemas. Main character context energy ๐Ÿ‘‘

Explode modifiers keep the syntax brief โœจ Like, a resource that gives you a geographic map for a street address might accept a hundred different combos of address fields, including partial addresses (just the city or postal code, for example) ๐Ÿ˜ญ That resource could be described with a template that lists every address component in order, OR with a much simpler template that uses an explode modifier, like this:

/mapper{?address*}

Then you pair that with some context that defines what the variable named "address" can include, like a reference to another addressing standard (e.g., [UPU-S42]). Once a recipient knows the schema, it can make the right expansions, like this:

/mapper?city=Newport%20Beach&state=CA

Expansion for exploded variables depends on both the operator being used and whether the composite gets treated as a list of values or as an associative array of (name, value) pairs. Structures get processed as if they were associative arrays, with names matching the fields in the structure definition, and "." separators showing the name hierarchy in substructures ๐Ÿ’–

If a variable is a composite structure and only some of its fields have defined values, then only the defined pairs show up in the expansion. This is super useful for templates with a huge pile of potential query terms ๐Ÿซถ

An explode modifier on a list variable makes the expansion loop over the list's member values. For path and query parameter expansions, each member value gets paired with the variable's name as a (varname, value) pair. That's how path and query parameters can repeat for multiple values, like so:

Given the variable assignments

   year  := ("1965", "2000", "2012")
   dom   := ("example", "com")
   Example Template     Expansion

   find{?year*}       find?year=1965&year=2000&year=2012
   www{.dom*}         www.example.com

3. Expansion โœจ

The whole URI Template expansion process is basically scanning the template string from beginning to end, copying literal characters over and swapping each expression for the result of applying its operator to the value of each variable in that expression ๐Ÿฆ‹ Each variable's value MUST be formed prior to template expansion.

The requirements for expansion of each part of the URI Template grammar live in this section ๐Ÿ’… There's also a non-normative algorithm for the expansion process as a whole in Appendix A ๐ŸŒธ

If a template processor runs into a character sequence outside an expression that doesn't match the <URI-Template> grammar, processing of the template SHOULD cease. The URI reference result SHOULD contain the expanded part of the template followed by the unexpanded remainder, and the location and type of error SHOULD be indicated to the invoking application ๐Ÿ™ˆ

If there's an error inside an expression, like an operator or value modifier the template processor doesn't recognize or doesn't support yet, or a character that isn't allowed by the <expression> grammar, then the unprocessed parts of the expression SHOULD be copied to the result unexpanded. Processing of the rest of the template SHOULD continue, and the location and type of error SHOULD be indicated to the invoking application โญ

If an error happens, the result might not be a valid URI reference. It'll be an incompletely expanded template string that's only meant for diagnostic use ๐Ÿ’€

3.1. Literal Expansion

If the literal character is allowed anywhere in the URI syntax (unreserved / reserved / pct-encoded), it gets copied straight into the result string ๐Ÿ“ Otherwise, the pct-encoded equivalent of the literal character gets copied in. To get there, you first encode the character as its sequence of octets in UTF-8, then encode each of those octets as a pct-encoded triplet โœจ

3.2. Expression Expansion

Each expression starts at an opening brace ({) and keeps going until the next closing brace (}) ๐ŸŒˆ Expressions can't be nested, no exceptions ๐Ÿ™…โ€โ™€๏ธ

To expand an expression, you figure out its expression type, then follow that type's expansion process for each comma-separated varspec in the expression. Level 1 templates are limited to the default operator (simple string value expansion) and a single variable per expression. Level 2 templates are limited to a single varspec per expression ๐Ÿง

The expression type comes from looking at the first character after the opening brace. If that character is an operator, remember the expression type tied to that operator for later expansion decisions, then skip to the next character to reach the variable-list. If the first character isn't an operator, the expression type is simple string expansion, and that first character is already the start of the variable-list ๐Ÿฆ‹

The examples in the subsections below use these definitions for variable values:

      count := ("one", "two", "three")
      dom   := ("example", "com")
      dub   := "me/too"
      hello := "Hello World!"
      half  := "50%"
      var   := "value"
      who   := "fred"
      base  := "http://example.com/home/"
      path  := "/foo/bar"
      list  := ("red", "green", "blue")
      keys  := [("semi",";"),("dot","."),("comma",",")]
      v     := "6"
      x     := "1024"
      y     := "768"
      empty := ""
      empty_keys  := []
      undef := null

3.2.1. Variable Expansion

A variable that is undefined (Section 2.3) has no value and gets ignored by the expansion process. If every variable in an expression is undefined, the expression's expansion is the empty string ๐Ÿซง

Expanding a defined, non-empty value gives you a substring of allowed URI characters. As described in Section 1.6, the expansion process is defined in terms of Unicode code points, so non-ASCII characters get pct-encoded consistently in the resulting URI reference ๐ŸŒ One way for a template processor to get a consistent expansion is to transcode the value string to UTF-8 (if it isn't already in UTF-8), then transform each octet that isn't in the allowed set into its pct-encoded triplet. Another way is to map directly from the value's native character encoding to the set of allowed URI characters, with any leftover disallowed characters mapping to the pct-encoded triplets for their UTF-8 octets [RFC3629] ๐Ÿ’œ

The allowed set depends on the expression type. Reserved (+) and fragment (#) expansions let the union of (unreserved / reserved / pct-encoded) pass through without pct-encoding. Every other expression type only lets unreserved characters pass through without pct-encoding. Heads up: the percent character (%) is only allowed as part of a pct-encoded triplet, and only for reserved/fragment expansion. In every other case, a value character of "%" MUST be pct-encoded as %25 during variable expansion ๐Ÿšจ

If a variable shows up more than once in an expression, or across multiple expressions in a URI Template, its value MUST stay static throughout the expansion process. That means the variable has the same value every time its expansion gets calculated ๐Ÿ”’ However, if reserved characters or pct-encoded triplets are in the value, some expression types will pct-encode them and others won't ๐Ÿ™ƒ

For a variable that's a simple string value, expansion just means appending the encoded value to the result string. An explode modifier has no effect here. A prefix modifier limits the expansion to the first max-length characters of the decoded value. If the value has multi-octet or pct-encoded characters, be careful not to split the value in the middle of a character: count each Unicode code point as one character ๐Ÿงฉ

For a variable that's an associative array, expansion depends on both the expression type and whether there's an explode modifier. Without an explode modifier, expansion means appending a comma-separated concatenation of each (name, value) pair that has a defined value. With an explode modifier, expansion appends each pair that has a defined value as either name=value or, if the value is the empty string and the expression type doesn't indicate form-style parameters (meaning it's not a ? or & type), simply name. Both name and value strings get encoded the same way as simple string values. A separator string goes between defined pairs depending on the expression type, as set out in this table:

   Type    Separator
              ","     (default)
     +        ","
     #        ","
     .        "."
     /        "/"
     ;        ";"
     ?        "&"
     &        "&"

For a variable that's a list of values, expansion depends on both the expression type and whether there's an explode modifier. Without an explode modifier, the expansion is a comma-separated concatenation of the defined member string values. With an explode modifier and an expression type that expands named parameters (;, ?, or &), the list gets expanded as if it were an associative array where each member value is paired with the list's varname. Otherwise, the value gets expanded as if it were a list of separate variable values, each separated by the expression type's associated separator from the table above ๐ŸŽ€

     Example Template     Expansion

       {count}            one,two,three
       {count*}           one,two,three
       {/count}           /one,two,three
       {/count*}          /one/two/three
       {;count}           ;count=one,two,three
       {;count*}          ;count=one;count=two;count=three
       {?count}           ?count=one,two,three
       {?count*}          ?count=one&count=two&count=three
       {&count*}          &count=one&count=two&count=three

3.2.2. Simple String Expansion: {var} ๐ŸŽ€

okay so simple string expansion is the default when there's no operator at all ๐Ÿ’–

For each defined variable in the variable-list, you run variable expansion (see Section 3.2.1 ๐Ÿ’…), and the only characters you get to leave unencoded are the ones in the unreserved set. If more than one variable has a defined value, you slap a comma (",") onto the result string as a separator between the expansions ๐Ÿซถ

Example Template Expansion

{var} value {hello} Hello%20World%21 {half} 50%25 O{empty}X OX O{undef}X OX {x,y} 1024,768 {x,hello,y} 1024,Hello%20World%21,768 ?{x,empty} ?1024, ?{x,undef} ?1024 ?{undef,y} ?768 {var:3} val {var:30} value {list} red,green,blue {list*} red,green,blue {keys} semi,%3B,dot,.,comma,%2C {keys*} semi=%3B,dot=.,comma=%2C

3.2.3. Reserved Expansion: {+var} ๐Ÿฆ‹

reserved expansion is giving "simple string expansion, but make it a little more chaotic" ๐Ÿ˜ญ It's triggered by the plus ("+") operator for Level 2 and above templates, and it's identical to simple string expansion except the substituted values are allowed to contain pct-encoded triplets and characters from the reserved set ๐ŸŒˆ

For each defined variable in the variable-list, you run variable expansion (Section 3.2.1 again, bestie ๐Ÿ’œ), but this time the allowed characters are (unreserved / reserved / pct-encoded). Same deal as before: if more than one variable has a defined value, you add a comma (",") between them โœจ

Example Template Expansion

{+var} value {+hello} Hello%20World! {+half} 50%25

{base}index http%3A%2F%2Fexample.com%2Fhome%2Findex {+base}index http://example.com/home/index O{+empty}X OX O{+undef}X OX

{+path}/here /foo/bar/here here?ref={+path} here?ref=/foo/bar up{+path}{var}/here up/foo/barvalue/here {+x,hello,y} 1024,Hello%20World!,768 {+path,x}/here /foo/bar,1024/here

{+path:6}/here /foo/b/here {+list} red,green,blue {+list*} red,green,blue {+keys} semi,;,dot,.,comma,, {+keys*} semi=;,dot=.,comma=,

3.2.4. Fragment Expansion: {#var} ๐Ÿงš

fragment expansion is the crosshatch ("#") operator, and it's for Level 2 and above templates ๐ŸŒธ It's basically reserved expansion, with one extra move: a crosshatch (the fragment delimiter) gets stuck on the front of the result string first, but only if at least one of the variables is defined ๐Ÿ™ˆ

Example Template Expansion

{#var} #value {#hello} #Hello%20World! {#half} #50%25 foo{#empty} foo# foo{#undef} foo {#x,hello,y} #1024,Hello%20World!,768 {#path,x}/here #/foo/bar,1024/here {#path:6}/here #/foo/b/here {#list} #red,green,blue {#list*} #red,green,blue {#keys} #semi,;,dot,.,comma,, {#keys*} #semi=;,dot=.,comma=,

3.2.5. Label Expansion with Dot-Prefix: {.var} ๐Ÿ“

label expansion uses the dot (".") operator, which is for Level 3 and above templates. It's super useful for describing URI spaces with changing domain names or path selectors, like filename extensions ๐ŸŒˆ

For each defined variable in the variable-list, you tack a "." onto the result string and then do variable expansion (Section 3.2.1 ๐Ÿ’…) with the allowed characters being the unreserved set.

Since "." is in the unreserved set, a value containing a "." ends up adding multiple labels. Iconic behaviour ๐Ÿ˜ญ

Example Template Expansion

{.who} .fred {.who,who} .fred.fred {.half,who} .50%25.fred www{.dom*} www.example.com X{.var} X.value X{.empty} X. X{.undef} X X{.var:3} X.val X{.list} X.red,green,blue X{.list*} X.red.green.blue X{.keys} X.semi,%3B,dot,.,comma,%2C X{.keys*} X.semi=%3B.dot=..comma=%2C X{.empty_keys} X X{.empty_keys*} X

3.2.6. Path Segment Expansion: {/var} ๐ŸŒธ

path segment expansion uses the slash ("/") operator in Level 3 and above templates, and it's perfect for describing URI path hierarchies ๐Ÿ›ค๏ธ

For each defined variable in the variable-list, you append "/" to the result string and then do variable expansion (Section 3.2.1 ๐Ÿซถ) with the allowed characters being the unreserved set.

Okay, the big tea here: path segment expansion works the exact same way as label expansion, except it uses "/" instead of ".". BUT unlike the dot, a "/" is a reserved character, so if it shows up in a value it gets pct-encoded ๐Ÿ’€ Not me being like "wait, so the slash doesn't just slide through?" ๐Ÿ™ˆ

Example Template Expansion

{/who} /fred {/who,who} /fred/fred {/half,who} /50%25/fred {/who,dub} /fred/me%2Ftoo {/var} /value {/var,empty} /value/ {/var,undef} /value {/var,x}/here /value/1024/here {/var:1,var} /v/value {/list} /red,green,blue {/list*} /red/green/blue {/list*,path:4} /red/green/blue/%2Ffoo {/keys} /semi,%3B,dot,.,comma,%2C {/keys*} /semi=%3B/dot=./comma=%2C

3.2.7. Path-Style Parameter Expansion: {;var} ๐Ÿฆ‹

path-style parameter expansion uses the semicolon (";") operator in Level 3 and above templates. It's what you reach for when you want URI path parameters, like path;property or path;name=value ๐Ÿ’œ

For each defined variable in the variable-list:

  • append ";" to the result string;
  • if the variable has a simple string value or no explode modifier is given, then:
    • append the variable name (encoded as if it were a literal string) to the result string;
    • if the variable's value is not empty, append "=" to the result string;
  • perform variable expansion (Section 3.2.1 โœจ) with the allowed characters being the unreserved set.

Example Template Expansion

{;who} ;who=fred {;half} ;half=50%25 {;empty} ;empty {;v,empty,who} ;v=6;empty;who=fred {;v,bar,who} ;v=6;who=fred {;x,y} ;x=1024;y=768 {;x,y,empty} ;x=1024;y=768;empty {;x,y,undef} ;x=1024;y=768 {;hello:5} ;hello=Hello {;list} ;list=red,green,blue {;list*} ;list=red;list=green;list=blue {;keys} ;keys=semi,%3B,dot,.,comma,%2C {;keys*} ;semi=%3B;dot=.;comma=%2C

3.2.8. Form-Style Query Expansion: {?var} ๐Ÿ’…

form-style query expansion uses the question-mark ("?") operator in Level 3 and above templates, and it's the move when you want to describe a whole optional query component all at once ๐ŸŒˆ

For each defined variable in the variable-list:

  • append "?" to the result string if this is the first defined value, or append "&" thereafter;
  • if the variable has a simple string value or no explode modifier is given, append the variable name (encoded as if it were a literal string) and an equals character ("=") to the result string; and,
  • perform variable expansion (Section 3.2.1 ๐ŸŽ€) with the allowed characters being the unreserved set.

Example Template Expansion

{?who} ?who=fred {?half} ?half=50%25 {?x,y} ?x=1024&y=768 {?x,y,empty} ?x=1024&y=768&empty= {?x,y,undef} ?x=1024&y=768 {?var:3} ?var=val {?list} ?list=red,green,blue {?list*} ?list=red&list=green&list=blue {?keys} ?keys=semi,%3B,dot,.,comma,%2C {?keys*} ?semi=%3B&dot=.&comma=%2C

3.2.9. Form-Style Query Continuation: {&var} ๐Ÿ‘‘

form-style query continuation uses the ampersand ("&") operator in Level 3 and above templates. It's for when your template already has a literal query component with fixed parameters, and you want to tack on optional &name=value pairs ๐Ÿซถ

For each defined variable in the variable-list:

  • append "&" to the result string;
  • if the variable has a simple string value or no explode modifier is given, append the variable name (encoded as if it were a literal string) and an equals character ("=") to the result string; and,
  • perform variable expansion (Section 3.2.1 โœจ) with the allowed characters being the unreserved set.

Example Template Expansion

{&who} &who=fred {&half} &half=50%25 ?fixed=yes{&x} ?fixed=yes&x=1024 {&x,y,empty} &x=1024&y=768&empty= {&x,y,undef} &x=1024&y=768

{&var:3} &var=val {&list} &list=red,green,blue {&list*} &list=red&list=green&list=blue {&keys} &keys=semi,%3B,dot,.,comma,%2C {&keys*} &semi=%3B&dot=.&comma=%2C

4. Security Considerations ๐Ÿ”ฅ

okay bestie, this part is SERIOUS ๐Ÿ™ˆ A URI Template doesn't contain active or executable content. HOWEVER, it might be possible to craft unexpected URIs if an attacker gets control over the template, or over the variable values inside an expression that allows reserved characters in the expansion. Either way, the security picture depends on who provides the template, who provides the values for the variables, what execution context the expansion happens in (client or server), and where the resulting URIs get used ๐Ÿ˜ญ

This specification doesn't limit where URI Templates can be used. Current implementations live inside server-side development frameworks and inside client-side javascript for computed links or forms ๐Ÿ’œ

Inside frameworks, templates usually act as guides for where data might show up in later (request-time) URIs in client requests. So the security concerns aren't really in the templates themselves, but in how the server pulls out and processes the user-provided data inside a normal Web request ๐ŸŒธ

On the client side, a URI Template has a lot of the same properties as HTML forms, except it's limited to URI characters and might be included in HTTP header field values instead of just message body content. Take extra care to make sure potentially dangerous URI reference strings, like ones beginning with "javascript:", don't show up in the expansion unless both the template and the values come from a trusted source. Major red flag ๐Ÿšฉ

Other security considerations are the same as those for URIs, as described in Section 7 of [RFC3986]. Go read that one too, it's a whole thing ๐Ÿ’€

5. Acknowledgments ๐Ÿซถ

the following people made contributions to this specification: Mike Burrows, Michaeljohn Clement, DeWitt Clinton, John Cowan, Stephen Farrell, Robbie Gates, Vijay K. Gurbani, Peter Johanson, Murray S. Kucherawy, James H. Manger, Tom Petch, Marc Portier, Pete Resnick, James Snell, and Jiankang Yao. Thank you all, you're literally the reason this works โœจ๐Ÿ‘‘

6. References ๐Ÿ“š

6.1. Normative References โœจ

okay so these are the docs this spec literally can't function without, bestie ๐Ÿ“š๐Ÿ’–

[ASCII] American National Standards Institute, "Coded Character Set - 7-bit American Standard Code for Information Interchange", ANSI X3.4, 1986.

[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.

[RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO 10646", STD 63, RFC 3629, November 2003.

[RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform Resource Identifier (URI): Generic Syntax", STD 66, RFC 3986, January 2005.

[RFC3987] Duerst, M. and M. Suignard, "Internationalized Resource Identifiers (IRIs)", RFC 3987, January 2005.

[RFC5234] Crocker, D. and P. Overell, "Augmented BNF for Syntax Specifications: ABNF", STD 68, RFC 5234, January 2008.

[RFC6365] Hoffman, P. and J. Klensin, "Terminology Used in Internationalization in the IETF", BCP 166, RFC 6365, September 2011.

[UNIV6] The Unicode Consortium, "The Unicode Standard, Version 6.0.0", (Mountain View, CA: The Unicode Consortium, 2011. ISBN 978-1-936213-01-6), http://www.unicode.org/versions/Unicode6.0.0/.

[UTR15] Davis, M. and M. Duerst, "Unicode Normalization Forms", Unicode Standard Annex # 15, April 2003, http://www.unicode.org/unicode/reports/tr15/tr15-23.html.

6.2. Informative References ๐Ÿฆ‹

these ones are just extra reading that's nice to know, not required to get the spec ๐Ÿงš

[OpenSearch] Clinton, D., "OpenSearch 1.1", Draft 5, December 2011, http://www.opensearch.org/Specifications/OpenSearch.

[UPU-S42] Universal Postal Union, "International Postal Address Components and Templates", UPU S42-1, November 2002, http://www.upu.int/en/activities/addressing/standards.html.

[WADL] Hadley, M., "Web Application Description Language", World Wide Web Consortium Member Submission SUBM-wadl-20090831, August 2009, http://www.w3.org/Submission/2009/SUBM-wadl-20090831/.

[WSDL] Weerawarana, S., Moreau, J., Ryman, A., and R. Chinnici, "Web Services Description Language (WSDL) Version 2.0 Part 1: Core Language", World Wide Web Consortium Recommendation REC-wsdl20-20070626, June 2007, http://www.w3.org/TR/2007/REC-wsdl20-20070626.


Appendix A. Implementation Hints ๐Ÿ”ฎ

okay so this part is totally NOT normative, it's just a vibe check on how you could actually build this thing ๐Ÿ’… the normative sections describe each operator with its own separate expansion process, but only so the descriptions stay clear ๐Ÿซถ in real implementations, the expressions are expected to get processed left-to-right with one common algorithm that only has minor tweaks per operator. this appendix walks through one such algorithm โœจ

Start with an empty result string and its non-error state.

Scan the template and copy the literals over to the result string (as in Section 3.1) until one of these happens: an expression shows up, marked by a {; an error shows up, which is when there's a non-literals character that isn't {; or the template just ends. When it ends, hand back the result string plus its current error or non-error state.

  • If you find an expression, scan the template up to the next } and pull out the characters in between the braces.
  • If the template ends before you hit a }, append the { and the extracted characters to the result string, then return with an error status saying the expression is malformed.

Next, look at the first character of the extracted expression to find the operator.

  • If the expression is just {}, or the operator is unknown or unimplemented, or the character isn't in the varchar set (Section 2.3), then append {, the extracted expression, and } to the result string. Remember that the result is in an error state, then go back and scan the rest of the template.
  • If you find a known and implemented operator, store it and skip ahead to the next character to start the varspec-list.
  • Otherwise, store the operator as NUL, which means simple string expansion.

Now use this value table to figure out how each expression type behaves. The "first" entry is the string you add to the result first if any of the expression's variables are defined. The "sep" entry is the separator you add before the second (or any later) defined variable expansion. The "named" entry is a boolean for whether the expansion includes the variable or key name when there's no explode modifier. The "ifemp" entry is a string to append to the name if its value is empty. The "allow" entry says which characters can go through unencoded in the value expansion. (U) means any character outside the unreserved set gets encoded. (U+R) means any character outside the union of (unreserved / reserved / pct-encoding) gets encoded. For both cases, each disallowed character is first encoded as its sequence of UTF-8 octets, then each octet becomes a pct-encoded triplet ๐Ÿ“

.------------------------------------------------------------------.
|          NUL     +      .       /       ;      ?      &      #   |
|------------------------------------------------------------------|
| first |  ""     ""     "."     "/"     ";"    "?"    "&"    "#"  |
| sep   |  ","    ","    "."     "/"     ";"    "&"    "&"    ","  |
| named | false  false  false   false   true   true   true   false |
| ifemp |  ""     ""     ""      ""      ""     "="    "="    ""   |
| allow |   U     U+R     U       U       U      U      U     U+R  |
`------------------------------------------------------------------'

With that table in mind, process the variable-list like this:

For each varspec, pull out a variable name and an optional modifier by scanning the variable-list until you hit a character that isn't in the varname set or you reach the end of the expression.

  • If you've reached the end of the expression and the varname is empty, go back and scan the rest of the template.
  • If you haven't reached the end and the last character you found is a modifier (* or :), remember that modifier. If it's an explode (*), scan the next character. If it's a prefix (:), keep scanning the next one to four characters for the max-length, written as a decimal integer. Then, if you still aren't at the end of the expression, scan the next character.
  • If you haven't reached the end and the last character you found isn't a comma (,), append {, the stored operator (if there is one), the scanned varname and modifier, the remaining expression, and } to the result string. Remember that the result is in an error state, then go back and scan the rest of the template.

Look up the value for the scanned variable name, and then:

  • If the varname is unknown, or it matches a variable with an undefined value (Section 2.3), skip to the next varspec.
  • If this is the first defined variable for this expression, append the "first" string for this expression type to the result string and remember you've done it. Otherwise, append the "sep" string to the result string.
  • If this variable's value is a string, then:
    • If named is true, append the varname to the result string using the same encoding process as literals, and then:
      • If the value is empty, append the "ifemp" string to the result string and skip to the next varspec.
      • Otherwise, append = to the result string.
    • If a prefix modifier is present and the prefix length is less than the value string's length in Unicode characters, append that many characters from the start of the value string to the result string. Pct-encode any characters that aren't in the "allow" set, and be careful not to split multi-octet or pct-encoded triplet characters that stand for a single Unicode code point.
    • Otherwise, append the value to the result string after pct-encoding any characters that aren't in the "allow" set.
  • Else, if no explode modifier is given, then:
    • If named is true, append the varname to the result string using the same encoding process as literals, and then:
      • If the value is empty, append the "ifemp" string to the result string and skip to the next varspec.
      • Otherwise, append = to the result string.
    • If this variable's value is a list, append each defined list member to the result string, pct-encoding any characters that aren't in the "allow" set, with a comma (,) between each defined member.
    • If this variable's value is an associative array or any other kind of paired (name, value) structure, append each pair with a defined value to the result string as name,value, pct-encoding any characters that aren't in the "allow" set, with a comma (,) between each defined pair.
  • Else, if an explode modifier is given, then:
    • If named is true, then for each defined list member or (name, value) pair with a defined value, do this:
      • If it isn't the first defined member or value, append the "sep" string to the result string.
      • If this is a list, append the varname to the result string using the same encoding process as literals.
      • If this is a pair, append the name to the result string using the same encoding process as literals.
      • If the member or value is empty, append the "ifemp" string to the result string. Otherwise, append = and the member or value to the result string, pct-encoding any characters in it that aren't in the "allow" set.
    • Else, if named is false, then:
      • If this is a list, append each defined list member to the result string, pct-encoding any characters that aren't in the "allow" set, with the "sep" string between each defined member.
      • If this is an array of (name, value) pairs, append each pair with a defined value to the result string as name=value, pct-encoding any characters that aren't in the "allow" set, with the "sep" string between each defined pair.

Once the variable-list for this expression is used up, go back and scan the rest of the template ๐ŸŽ€

Authors' Addresses ๐ŸŒธ

these are the humans who wrote it, so iconic ๐Ÿซถ

Joe Gregorio
Google

EMail: joe@bitworking.org
URI:   http://bitworking.org/


Roy T. Fielding
Adobe Systems Incorporated

EMail: fielding@gbiv.com
URI:   http://roy.gbiv.com/


Marc Hadley
The MITRE Corporation

EMail: mhadley@mitre.org
URI:   http://mitre.org/


Mark Nottingham
Rackspace

EMail: mnot@mnot.net
URI:   http://www.mnot.net/


David Orchard
Salesforce.com

EMail: orchard@pacificspirit.com
URI:   http://www.pacificspirit.com/
๐Ÿ“ฆ where this page comes from