3. Expansion (扩展)
URI模板扩展的过程是从头到尾扫描模板字符串,复制字面字符,并将每个表达式替换为将表达式的操作符应用于表达式中命名的每个变量的值的结果。每个变量的值必须 (MUST) 在模板扩展之前形成。
本节定义了URI模板语法各个方面的扩展要求。附录A提供了整个扩展过程的非规范算法。
如果模板处理器在表达式外遇到不匹配 <URI-Template> 语法的字符序列,则应该 (SHOULD) 停止模板的处理,URI引用结果应该 (SHOULD) 包含模板的已扩展部分,后跟未扩展的剩余部分,并且应该 (SHOULD) 向调用应用程序指示错误的位置和类型。
如果在表达式中遇到错误,例如模板处理器无法识别或尚不支持的操作符或值修饰符,或者发现 <expression> 语法不允许的字符,则表达式的未处理部分应该 (SHOULD) 被未扩展地复制到结果中,模板的其余部分的处理应该 (SHOULD) 继续,并且应该 (SHOULD) 向调用应用程序指示错误的位置和类型。
如果发生错误,返回的结果可能不是有效的URI引用;它将是一个不完全扩展的模板字符串,仅用于诊断目的。
3.1. Literal Expansion (字面扩展)
如果字面字符在URI语法中的任何位置都是允许的(unreserved / reserved / pct-encoded),则将其直接复制到结果字符串中。否则,字面字符的百分号编码等价物将被复制到结果字符串中,方法是首先将字符编码为其UTF-8中的八位字节序列,然后将每个这样的八位字节编码为百分号编码三元组。
3.2. Expression Expansion (表达式扩展)
每个表达式由一个左花括号(")字符表示,并持续到下一个右花括号(")。表达式不能嵌套。
表达式的扩展是通过确定其表达式类型,然后对表达式中的每个逗号分隔的varspec遵循该类型的扩展过程来完成的。Level 1模板仅限于默认操作符(简单字符串值扩展)和每个表达式一个变量。Level 2模板仅限于每个表达式一个varspec。
表达式类型通过查看左花括号后的第一个字符来确定。如果该字符是操作符,则记住与该操作符关联的表达式类型以供以后的扩展决策使用,并跳到下一个字符以获取变量列表。如果第一个字符不是操作符,则表达式类型为简单字符串扩展,第一个字符是变量列表的开头。
以下各小节中的示例使用以下变量值定义:
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 (变量扩展)
未定义(第2.3节)的变量没有值,并且被扩展过程忽略。如果表达式中的所有变量都未定义,则该表达式的扩展为空字符串。
已定义、非空值的变量扩展会产生允许的URI字符的子字符串。如第1.6节所述,扩展过程是根据Unicode代码点定义的,以确保在生成的URI引用中一致地对非ASCII字符进行百分号编码。模板处理器获得一致扩展的一种方法是将值字符串转码为UTF-8(如果它尚未是UTF-8),然后将不在允许集中的每个八位字节转换为相应的百分号编码三元组。另一种方法是直接从值的本机字符编码映射到允许的URI字符集,任何剩余的不允许字符映射到对应于该字符在UTF-8 [RFC3629] 中编码为八位字节时的百分号编码三元组序列。
给定扩展的允许集取决于表达式类型:保留("+")和片段("#")扩展允许(unreserved / reserved / pct-encoded)并集中的字符集在不进行百分号编码的情况下通过,而所有其他表达式类型仅允许非保留字符在不进行百分号编码的情况下通过。请注意,百分号字符("%")仅作为百分号编码三元组的一部分被允许,并且仅用于保留/片段扩展:在所有其他情况下,值字符"%"必须 (MUST) 通过变量扩展被百分号编码为"%25"。
如果变量在表达式中出现多次或在URI模板的多个表达式中出现,则该变量的值在整个扩展过程中必须 (MUST) 保持静态(即,该变量必须具有相同的值以计算每个扩展)。但是,如果值中出现保留字符或百分号编码三元组,某些表达式类型将对它们进行百分号编码,而其他表达式类型则不会。
对于简单字符串值的变量,扩展包括将编码值附加到结果字符串。爆炸修饰符没有效果。前缀修饰符将扩展限制为解码值的前max-length个字符。如果值包含多八位字节或百分号编码字符,则必须小心避免在字符中间拆分值:将每个Unicode代码点计为一个字符。
对于关联数组的变量,扩展取决于表达式类型和爆炸修饰符的存在。如果没有爆炸修饰符,扩展包括附加具有定义值的每个(name, value)对的逗号分隔连接。如果存在爆炸修饰符,扩展包括将具有定义值的每对附加为"name=value"或,如果值为空字符串且表达式类型不表示表单样式参数(即,不是"?"或"&"类型),则仅为"name"。name和value字符串都以与简单字符串值相同的方式编码。根据表达式类型,在定义的对之间附加分隔符字符串,如下表所定义:
类型 分隔符
"," (默认)
+ ","
# ","
. "."
/ "/"
; ";"
? "&"
& "&"
对于列表值的变量,如果没有爆炸修饰符,扩展包括附加具有定义值的每个列表成员值的逗号分隔连接,作为该变量的单个名称的值。如果存在爆炸修饰符,扩展包括将具有定义值的每个列表成员值附加为具有该变量名称的单独值,或者,对于没有命名变量的表达式类型(没有";"、"?"或"&"),只是附加每个值,它们之间由类型特定的分隔符分隔。
列表值或关联数组值的前缀修饰符没有效果。
3.2.2. Simple String Expansion: {var} (简单字符串扩展)
当没有给出操作符时,简单字符串扩展是默认的表达式类型。
对于变量列表中的每个已定义变量,按照第3.2.1节中的定义执行变量扩展,允许的字符为非保留集中的字符。如果多个变量具有已定义的值,则将逗号(",")附加到结果字符串作为变量扩展之间的分隔符。
示例模板 扩展结果
`{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} (保留扩展)
保留扩展,由Level 2及更高级别模板的加号("+")操作符表示,与简单字符串扩展相同,只是替换的值也可能包含百分号编码三元组和保留集中的字符。
对于变量列表中的每个已定义变量,按照第3.2.1节中的定义执行变量扩展,允许的字符为(unreserved / reserved / pct-encoded)集中的字符。如果多个变量具有已定义的值,则将逗号(",")附加到结果字符串作为变量扩展之间的分隔符。
示例模板 扩展结果
{+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
{+x,hello,y} 1024,Hello%20World!,768
{+path,x}/here /foo/bar,1024/here
{+path:6}/here /foo/b,1024/here
{+list} red,green,blue
{+list*} red,green,blue
{+keys} semi,;,dot,.,comma,,
{+keys*} semi=;,dot=.,comma=,
3.2.4. Fragment Expansion: \{#var} (片段扩展)
片段扩展,由Level 2及更高级别模板的井号("#")操作符表示,与保留扩展相同,只是如果任何变量已定义,则首先将井号字符(片段分隔符)附加到结果字符串。
示例模板 扩展结果
{#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} (带点前缀的标签扩展)
标签扩展,由Level 3及更高级别模板的点(".")操作符表示,对于描述具有不同域名或路径选择器(例如,文件扩展名)的URI空间很有用。
对于变量列表中的每个已定义变量,将"."附加到结果字符串,然后按照第3.2.1节中的定义执行变量扩展,允许的字符为非保留集中的字符。
由于"."在非保留集中,包含"."的值具有添加多个标签的效果。
示例模板 扩展结果
{.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} (路径段扩展)
路径段扩展,由Level 3及更高级别模板中的斜杠("/")操作符表示,对于描述URI路径层次结构很有用。
对于变量列表中的每个已定义变量,将"/"附加到结果字符串,然后按照第3.2.1节中的定义执行变量扩展,允许的字符为非保留集中的字符。
请注意,路径段扩展的扩展过程与标签扩展相同,除了用"/"代替"."。但是,与"."不同,"/"是保留字符,如果在值中找到,将被百分号编码。
示例模板 扩展结果
{/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} (路径样式参数扩展)
路径样式参数扩展,由Level 3及更高级别模板中的分号(";")操作符表示,对于描述URI路径参数很有用,例如"path;property"或"path;name=value"。
对于变量列表中的每个已定义变量:
- 将";"附加到结果字符串;
- 如果变量具有简单字符串值或没有给出爆炸修饰符,则:
- 将变量名(编码为字面字符串)附加到结果字符串;
- 如果变量的值不为空,将"="附加到结果字符串;
- 按照第3.2.1节中的定义执行变量扩展,允许的字符为非保留集中的字符。
示例模板 扩展结果
{;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} (表单样式查询扩展)
表单样式查询扩展,由Level 3及更高级别模板中的问号("?")操作符表示,对于描述整个可选查询组件很有用。
对于变量列表中的每个已定义变量:
- 如果这是第一个已定义的值,则将"?"附加到结果字符串,否则附加"&";
- 如果变量具有简单字符串值或没有给出爆炸修饰符,则将变量名(编码为字面字符串)和等号字符("=")附加到结果字符串;并且,
- 按照第3.2.1节中的定义执行变量扩展,允许的字符为非保留集中的字符。
示例模板 扩展结果
{?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} (表单样式查询延续)
表单样式查询延续,由Level 3及更高级别模板中的和号("&")操作符表示,对于在已包含具有固定参数的字面查询组件的模板中描述可选的&name=value对很有用。
对于变量列表中的每个已定义变量:
- 将"&"附加到结果字符串;
- 如果变量具有简单字符串值或没有给出爆炸修饰符,则将变量名(编码为字面字符串)和等号字符("=")附加到结果字符串;并且,
- 按照第3.2.1节中的定义执行变量扩展,允许的字符为非保留集中的字符。
示例模板 扩展结果
{&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