Skip to main content

3. Expansion (扩展)

URI Template扩展过程是从头到尾扫描模板字符串,复制字面字符,并用将表达式的操作符应用于表达式中命名的每个变量的值的结果替换每个表达式。在模板扩展之前,必须形成每个变量的值。

本节定义URI模板语法每个方面的扩展要求。附录A提供了整个扩展过程的非规范性算法。

如果模板处理器在表达式外遇到不匹配语法的字符序列,则应停止处理模板,URI引用结果应包含模板的扩展部分,后跟未扩展的剩余部分,并且应向调用应用程序指示错误的位置和类型。

如果在表达式中遇到错误,例如模板处理器无法识别或尚不支持的操作符或值修饰符,或者找到语法不允许的字符,则应将表达式的未处理部分未扩展地复制到结果,应继续处理模板的其余部分,并且应向调用应用程序指示错误的位置和类型。

如果发生错误,返回的结果可能不是有效的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代码点定义,以确保非ASCII字符在结果URI引用中一致地进行百分号编码。模板处理器获得一致扩展的一种方法是将值字符串转码为UTF-8(如果尚未是UTF-8),然后将不在允许集中的每个八位字节转换为相应的百分号编码三元组。另一种方法是直接从值的本机字符编码映射到允许的URI字符集,任何剩余的不允许字符映射到当编码为UTF-8 [RFC3629]时对应于该字符的八位字节的百分号编码三元组序列。

给定扩展的允许集取决于表达式类型:保留("+")和片段("#")扩展允许(unreserved / reserved / pct-encoded)联合中的字符集通过而不进行百分号编码,而所有其他表达式类型仅允许未保留字符通过而不进行百分号编码。请注意,百分号字符("%")仅作为百分号编码三元组的一部分允许,并且仅用于保留/片段扩展:在所有其他情况下,变量扩展必须将"%"的值字符编码为"%25"。

如果变量在表达式中或URI模板的多个表达式中出现多次,则该变量的值在整个扩展过程中必须保持静态(即,变量在计算每个扩展时必须具有相同的值)。但是,如果值中出现保留字符或百分号编码的三元组,它们将被某些表达式类型进行百分号编码,而不会被其他类型进行编码。

对于具有简单字符串值的变量,扩展包括将编码值附加到结果字符串。explode修饰符没有效果。前缀修饰符将扩展限制为解码值的前max-length个字符。如果值包含多八位字节或百分号编码的字符,则必须注意避免在字符中间分割:将每个Unicode代码点计为一个字符。

对于关联数组的变量,扩展取决于表达式类型和explode修饰符的存在。如果没有explode修饰符,扩展包括附加具有定义值的每个(name, value)对的逗号分隔串联。如果有explode修饰符,扩展包括将具有定义值的每个对附加为"name=value",或者如果值是空字符串且表达式类型不指示表单样式参数(即,不是"?"或"&"类型),则仅为"name"。name和value字符串都以与简单字符串值相同的方式编码。根据表达式类型在定义的对之间附加分隔符字符串,如下表所定义:

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

对于值列表的变量,扩展取决于表达式类型和explode修饰符的存在。如果没有explode修饰符,扩展包括定义的成员字符串值的逗号分隔串联。如果有explode修饰符且表达式类型扩展命名参数(";"、"?"或"&"),则列表被扩展为就像它是一个关联数组,其中每个成员值与列表的varname配对。否则,值将被扩展为就像它是单独变量值的列表,每个值由上表定义的表达式类型的相关分隔符分隔。

示例模板与扩展:

{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} (简单字符串扩展)

简单字符串扩展是未提供操作符时的默认表达式类型。

对于变量列表中的每个已定义变量,执行变量扩展,如第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
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} (片段扩展)

片段扩展,如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")很有用。

对于变量列表中的每个已定义变量:

  • 向结果字符串附加";";
  • 如果变量具有简单字符串值或未给出explode修饰符,则:
    • 将变量名称(编码为就像它是字面字符串一样)附加到结果字符串;
    • 如果变量的值不为空,则向结果字符串附加"=";
  • 执行变量扩展,如第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及以上模板的问号("?")操作符所指示的,对于描述整个可选查询组件很有用。

对于变量列表中的每个已定义变量:

  • 如果这是第一个定义的值,则向结果字符串附加"?",否则之后附加"&";
  • 如果变量具有简单字符串值或未给出explode修饰符,则将变量名称(编码为就像它是字面字符串一样)和等号字符("=")附加到结果字符串;以及,
  • 执行变量扩展,如第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对很有用。

对于变量列表中的每个已定义变量:

  • 向结果字符串附加"&";
  • 如果变量具有简单字符串值或未给出explode修饰符,则将变量名称(编码为就像它是字面字符串一样)和等号字符("=")附加到结果字符串;以及,
  • 执行变量扩展,如第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