RFC 6570 - URI Template (URI模板)
- 状态: Proposed Standard
- 发布日期: March 2012
- Stream: IETF
- 勘误: 无勘误
Abstract (摘要)
URI Template (URI模板) 是一个紧凑的字符序列,用于通过变量扩展描述一系列统一资源标识符.本规范定义了URI模板语法以及将URI模板扩展为URI引用的过程,以及在Internet上使用URI模板的指南.
Status of This Memo (本备忘录状态)
这是一个Internet标准跟踪文档.
本文档是Internet工程任务组(IETF)的产品.它代表了IETF社区的共识.它已经接受了公众审查,并已获得Internet工程指导组(IESG)的批准出版.有关Internet标准的更多信息,请参见RFC 5741第2节.
有关本文档当前状态、任何勘误表以及如何提供反馈的信息,可从http://www.rfc-editor.org/info/rfc6570获取.
Copyright Notice (版权声明)
Copyright (c) 2012 IETF Trust and the persons identified as the document authors. All rights reserved.
本文档受BCP 78和IETF信托有关IETF文档的法律规定(http://trustee.ietf.org/license-info)的约束,这些规定在本文档发布之日有效.请仔细阅读这些文档,因为它们描述了您对本文档的权利和限制.从本文档中提取的代码组件必须包含简化BSD许可证文本,如信托法律规定第4.e节所述,并按简化BSD许可证中所述不提供保证.
Table of Contents (目录)
主要章节
-
- 1.1. Overview (概述)
- 1.2. Levels and Expression Types (级别和表达式类型)
- 1.3. Design Considerations (设计考虑)
- 1.4. Limitations (限制)
- 1.5. Notational Conventions (符号约定)
- 1.6. Character Encoding and Unicode Normalization (字符编码和Unicode规范化)
-
- 2.1. Literals (字面量)
- 2.2. Expressions (表达式)
- 2.3. Variables (变量)
- 2.4. Value Modifiers (值修饰符)
- 2.4.1. Prefix Values (前缀值)
- 2.4.2. Composite Values (复合值)
-
- 3.1. Literal Expansion (字面扩展)
- 3.2. Expression Expansion (表达式扩展)
- 3.2.1. Variable Expansion (变量扩展)
- 3.2.2. Simple String Expansion: {var} (简单字符串扩展)
- 3.2.3. Reserved Expansion: {+var} (保留扩展)
- 3.2.4. Fragment Expansion: {#var} (片段扩展)
- 3.2.5. Label Expansion with Dot-Prefix: {.var} (带点前缀的标签扩展)
- 3.2.6. Path Segment Expansion: {/var} (路径段扩展)
- 3.2.7. Path-Style Parameter Expansion: {;var} (路径样式参数扩展)
- 3.2.8. Form-Style Query Expansion: {?var} (表单样式查询扩展)
- 3.2.9. Form-Style Query Continuation: {&var} (表单样式查询继续)
-
5. Acknowledgments (致谢)
-
- 6.1. Normative References (规范性参考文献)
- 6.2. Informative References (信息性参考文献)
附录
核心概念
URI模板示例
http://example.com/~{username}/
http://example.com/dictionary/{term:1}/{term}
http://example.com/search{?q,lang}
表达式类型
URI模板支持多种表达式操作符:
- 无操作符 - 简单字符串扩展:
{var} - + - 保留字符扩展:
{+var} - # - 片段标识符:
{#var} - . - 标签扩展:
{.var} - / - 路径段:
{/var} - ; - 路径参数:
{;var} - ? - 查询组件:
{?var} - & - 查询继续:
{&var}
级别系统
- Level 1: 基本字符串扩展
- Level 2: 保留字符和片段扩展
- Level 3: 多变量和路径/查询操作符
- Level 4: 值修饰符(前缀和explode)
相关RFC
- RFC 3986: URI Generic Syntax
- RFC 3987: Internationalized Resource Identifiers (IRIs)
- RFC 5234: Augmented BNF for Syntax Specifications
维护信息
RFC编号: 6570
类别: Standards Track
更新: 无
ISSN: 2070-1721
2. Syntax (语法)
URI Template (URI模板) 是一个可打印Unicode字符的字符串,包含零个或多个嵌入的变量表达式,每个表达式由一对匹配的大括号('{', '}')分隔.
URI-Template = *( literals / expression )
虽然模板(和模板处理器实现)在上面用四个渐进级别描述,但我们根据Level 4的ABNF定义URI-Template语法.限制于较低级别模板的模板处理器可以排除仅适用于较高级别的ABNF规则.但是,建议所有解析器实现完整语法,以便可以向最终用户正确识别不受支持的级别.
2.1. Literals (字面量)
URI模板字符串中表达式外的字符旨在按字面复制到URI引用,如果该字符在URI中允许(reserved / unreserved / pct-encoded),或者如果不允许,则作为对应于该字符UTF-8 [RFC3629]编码的百分号编码三元组序列复制到URI引用.
literals = %x21 / %x23-24 / %x26 / %x28-3B / %x3D / %x3F-5B
/ %x5D / %x5F / %x61-7A / %x7E / ucschar / iprivate
/ pct-encoded
; 任何Unicode字符,除了: CTL, SP,
; DQUOTE, "'", "%" (除了pct-encoded),
; "<", ">", "\", "^", "`", "{", "|", "}"
2.2. Expressions (表达式)
模板表达式是URI模板的参数化部分.每个表达式包含一个可选的操作符,它定义了表达式类型及其相应的扩展过程,后跟一个逗号分隔的变量说明符列表(变量名称和可选值修饰符).如果未提供操作符,则表达式默认为未保留值的简单变量扩展.
expression = "{" [ operator ] variable-list "}"
operator = op-level2 / op-level3 / op-reserve
op-level2 = "+" / "#"
op-level3 = "." / "/" / ";" / "?" / "&"
op-reserve = "=" / "," / "!" / "@" / "|"
操作符字符被选择以反映它们各自在URI通用语法中作为保留字符的角色.本规范第3节中定义的操作符包括:
+保留字符串;#以"#"为前缀的片段标识符;.以"."为前缀的名称标签或扩展名;/以"/"为前缀的路径段;;以";"为前缀的路径参数名称或name=value对;?以"?"开头的查询组件,由"&"分隔的name=value对组成;以及,&在字面查询组件内继续查询样式的&name=value对.
操作符字符等号("=")、逗号(",")、感叹号("!")、at符号("@")和竖线("|")保留用于将来扩展.
表达式语法特别排除了美元符号("$")和括号["("和")"]字符的使用,以便它们可以在本规范范围之外使用.例如,宏语言可能使用这些字符在将字符串作为URI模板处理之前对字符串应用宏替换.
2.3. Variables (变量)
在操作符(如果有)之后,每个表达式包含一个或多个逗号分隔的变量说明符(varspec)列表.变量名称具有多重目的:记录预期的值类型,用于在模板处理器中关联值的标识符,以及在name=value扩展中用于名称的字面字符串(除了在展开关联数组时).变量名称区分大小写,因为名称可能在区分大小写的URI组件中扩展.
variable-list = varspec *( "," varspec )
varspec = varname [ modifier-level4 ]
varname = varchar *( ["."] varchar )
varchar = ALPHA / DIGIT / "_" / pct-encoded
varname可能包含一个或多个百分号编码的三元组.这些三元组被视为变量名称的基本部分,在处理期间不会被解码.包含百分号编码字符的varname与解码了这些相同字符的varname不是同一个变量.提供URI模板的应用程序预期在变量名称中使用百分号编码时保持一致.
表达式可能引用模板处理器未知的变量或其值设置为特殊"未定义"值(如undef或null)的变量.这些未定义的变量在扩展过程中得到特殊处理(第3.2.1节).
长度为零的变量值不被视为未定义;它具有空字符串的定义值.
在Level 4模板中,变量可能具有列表值或(name, value)对关联数组形式的复合值.这些值类型不直接由模板语法指示,但它们确实对扩展过程有影响(第3.2.1节).
定义为列表值的变量,如果列表包含零个成员,则被视为未定义.定义为(name, value)对关联数组的变量,如果数组包含零个成员或数组中的所有成员名称都与未定义的值关联,则被视为未定义.
2.4. Value Modifiers (值修饰符)
Level 4模板表达式中的每个变量可以有一个修饰符,指示其扩展被限制为变量值字符串的前缀,或者其扩展被展开为值列表形式或(name, value)对关联数组形式的复合值.
modifier-level4 = prefix / explode
2.4.1. Prefix Values (前缀值)
前缀修饰符表示变量扩展被限制为变量值字符串的前缀.前缀修饰符通常用于分层分区标识符空间,如参考索引和基于哈希的存储中常见的那样.它还用于将扩展值限制为最大字符数.前缀修饰符不适用于具有复合值的变量.
prefix = ":" max-length
max-length = %x31-39 0*3DIGIT ; 正整数 < 10000
max-length是一个正整数,指从变量值作为Unicode字符串的开头开始的最大字符数.请注意,此编号以字符为单位,而不是八位字节,以避免在多八位字节编码字符的八位字节之间或百分号编码的三元组内分割.如果max-length大于变量值的长度,则使用整个值字符串.
例如,
给定变量赋值:
var := "value"
semi := ";"
示例模板与扩展:
{var} value
{var:20} value
{var:3} val
{semi} %3B
{semi:2} %3B
2.4.2. Composite Values (复合值)
explode("*")修饰符表示变量将被视为由值列表或(name, value)对关联数组组成的复合值.因此,扩展过程应用于复合的每个成员,就像它被列为单独的变量一样.这种变量规范的自我文档化程度明显低于非展开变量,因为变量名称与URI引用在扩展后的显示方式之间的对应关系较少.
explode = "*"
由于URI模板不包含类型或模式的指示,展开变量的类型被假定为由上下文确定.例如,处理器可能以区分值为字符串、列表或关联数组的形式提供值.同样,使用模板的上下文(脚本、标记语言、接口定义语言等)可能定义将变量名称与类型、结构或模式关联的规则.
explode修饰符提高了URI模板语法的简洁性.例如,接受给定街道地址的地理地图的资源可能接受地址输入字段的一百种排列,包括部分地址(例如,只有城市或邮政编码).这样的资源可以描述为一个模板,其中按顺序列出每个地址组件,或者使用更简单得多的模板来使用explode修饰符,如:
/mapper{?address*}
以及一些定义名为"address"的变量可以包含的内容的上下文,例如通过引用某些其他地址标准(例如,[UPU-S42]).了解模式的接收者然后可以提供适当的扩展,例如:
/mapper?city=Newport%20Beach&state=CA
展开变量的扩展过程取决于使用的操作符以及复合值是被视为值列表还是(name, value)对的关联数组.结构作为关联数组处理,其名称对应于结构定义中的字段,并使用"."分隔符指示子结构中的名称层次结构.
如果变量具有复合结构,并且该结构中只有部分字段具有定义的值,则扩展中仅存在定义的对.这对于由大量潜在查询项组成的模板很有用.
应用于列表变量的explode修饰符会导致扩展迭代列表的成员值.对于路径和查询参数扩展,每个成员值与变量名称配对作为(varname, value)对.这允许对多个值重复路径和查询参数,如:
给定变量赋值:
year := ("1965", "2000", "2012")
dom := ("example", "com")
示例模板与扩展:
find{?year*} find?year=1965&year=2000&year=2012
www{.dom*} www.example.com
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
4. Security Considerations (安全考虑)
URI Template不包含活动或可执行内容.但是,如果攻击者获得对模板或表达式内允许保留字符扩展的变量值的控制,则可能制作意外的URI.在任一情况下,安全考虑主要由谁提供模板、谁提供模板中变量使用的值、扩展发生在什么执行上下文(客户端或服务器)以及结果URI在何处使用来确定.
本规范不限制URI模板可能使用的位置.当前的实现存在于服务器端开发框架内,以及客户端javascript中用于计算链接或表单.
在框架内,模板通常充当数据可能在稍后(请求时)客户端请求中的URI内出现的位置的指南.因此,安全问题不在模板本身,而在服务器如何在正常Web请求中提取和处理用户提供的数据.
在客户端实现中,URI模板具有许多与HTML表单相同的属性,除了限于URI字符,并且可能包含在HTTP头字段值中而不仅仅是消息正文内容.应注意确保潜在危险的URI引用字符串(如以"javascript:"开头的字符串)不会出现在扩展中,除非模板和值都由可信来源提供.
其他安全考虑与URI的安全考虑相同,如[RFC3986]第7节所述.
Appendix A. Implementation Hints (附录A. 实现提示)
关于扩展的规范性章节为了描述清晰,使用单独的扩展过程描述每个操作符.在实际实现中,我们期望使用通用算法从左到右处理表达式,每个操作符的过程仅有微小变化.本非规范性附录描述了一种这样的算法.
初始化一个空结果字符串及其非错误状态.
扫描模板并将字面量复制到结果字符串(如第3.1节所述),直到"{"指示表达式、除"{"之外的非字面量字符指示错误,或模板结束.当它结束时,返回结果字符串及其当前错误或非错误状态.
- 如果找到表达式,扫描模板到下一个"}"并提取大括号之间的字符.
- 如果模板在"}"之前结束,则将"{"和提取的字符附加到结果字符串并返回错误状态,指示表达式格式错误.
检查提取表达式的第一个字符以查找操作符.
- 如果表达式结束(即,是"{}")、找到未知或未实现的操作符,或字符不在varchar集中(第2.3节),则将"{"、提取的表达式和"}"附加到结果字符串,记住结果处于错误状态,然后返回扫描模板的其余部分.
- 如果找到已知且已实现的操作符,则存储操作符并跳到下一个字符以开始varspec-list.
- 否则,将操作符存储为NUL(简单字符串扩展).
使用以下值表根据表达式类型操作符确定处理行为."first"的条目是如果表达式的任何变量已定义,则首先附加到结果的字符串."sep"的条目是在任何第二个(或后续)已定义变量扩展之前附加到结果的分隔符."named"的条目是一个布尔值,指示当未给出explode修饰符时,扩展是否包括变量或键名."ifemp"的条目是如果其对应值为空时要附加到名称的字符串."allow"的条目指示在值扩展中允许未编码的字符:(U)表示不在未保留集中的任何字符将被编码;(U+R)表示不在(unreserved / reserved / pct-encoding)联合中的任何字符将被编码;对于这两种情况,每个不允许的字符首先编码为其在UTF-8中的八位字节序列,然后将每个这样的八位字节编码为百分号编码的三元组.
.------------------------------------------------------------------.
| NUL + . / ; ? & # |
|------------------------------------------------------------------|
| first "" "" "." "/" ";" "?" "&" "#" |
| sep "," "," "." "/" ";" "&" "&" "," |
| named false false false false true true true false |
| ifemp "" "" "" "" "" "=" "=" "" |
| allow U U+R U U U U U U+R |
`------------------------------------------------------------------'
记住上表,按如下方式处理变量列表:
对于每个varspec,通过扫描变量列表直到找到不在varname集中的字符或到达表达式结束,从表达式中提取变量名称和可选修饰符.
- 如果是表达式的结束且varname为空,则返回扫描模板的其余部分.
- 如果不是表达式的结束且找到的最后一个字符指示修饰符(""或":"),则记住该修饰符.如果是explode(""),扫描下一个字符.如果是prefix(":"),继续扫描接下来的一到四个字符以获取表示为十进制整数的max-length,然后,如果仍不是表达式的结束,扫描下一个字符.
- 如果不是表达式的结束且找到的最后一个字符不是逗号(","),则将"{"、存储的操作符(如果有)、扫描的varname和修饰符、剩余的表达式和"}"附加到结果字符串,记住结果处于错误状态,然后返回扫描模板的其余部分.
查找扫描的变量名称的值,然后
- 如果varname未知或对应于具有未定义值的变量(第2.3节),则跳到下一个varspec.
- 如果这是此表达式的第一个已定义变量,则将此表达式类型的first字符串附加到结果字符串并记住已完成.否则,将sep字符串附加到结果字符串.
- 如果此变量的值是字符串,则
- 如果named为true,则使用与字面量相同的编码过程将varname附加到结果字符串,并
- 如果值为空,则将ifemp字符串附加到结果字符串并跳到下一个varspec;
- 否则,将"="附加到结果字符串.
- 如果存在prefix修饰符且prefix长度小于值字符串长度(以Unicode字符数计),则将该数量的字符从值字符串的开头附加到结果字符串,在对不在allow集中的任何字符进行百分号编码之后,同时注意不要分割表示单个Unicode代码点的多八位字节或百分号编码的三元组字符;
- 否则,在对不在allow集中的任何字符进行百分号编码之后,将值附加到结果字符串.
- 如果named为true,则使用与字面量相同的编码过程将varname附加到结果字符串,并
- 否则,如果未给出explode修饰符,则
- 如果named为true,则使用与字面量相同的编码过程将varname附加到结果字符串,并
- 如果值为空,则将ifemp字符串附加到结果字符串并跳到下一个varspec;
- 否则,将"="附加到结果字符串;并
- 如果此变量的值是列表,则将每个已定义的列表成员附加到结果字符串,在对不在allow集中的任何字符进行百分号编码之后,在每个已定义列表成员之间向结果附加逗号(",");
- 如果此变量的值是关联数组或任何其他形式的配对(name, value)结构,则将每个具有定义值的对作为"name,value"附加到结果字符串,在对不在allow集中的任何字符进行百分号编码之后,在每个已定义对之间向结果附加逗号(",").
- 如果named为true,则使用与字面量相同的编码过程将varname附加到结果字符串,并
- 否则,如果给出explode修饰符,则
- 如果named为true,则对于每个已定义的列表成员或具有定义值的数组(name, value)对,执行:
- 如果这不是第一个已定义的成员/值,则将sep字符串附加到结果字符串;
- 如果这是列表,则使用与字面量相同的编码过程将varname附加到结果字符串;
- 如果这是对,则使用与字面量相同的编码过程将name附加到结果字符串;
- 如果成员/值为空,则将ifemp字符串附加到结果字符串;否则,在对任何不在allow集中的成员/值字符进行百分号编码之后,将"="和成员/值附加到结果字符串.
- 否则,如果named为false,则
- 如果这是列表,则将每个已定义的列表成员附加到结果字符串,在对不在allow集中的任何字符进行百分号编码之后,在每个已定义列表成员之间将sep字符串附加到结果.
- 如果这是(name, value)对数组,则将每个具有定义值的对作为"name=value"附加到结果字符串,在对不在allow集中的任何字符进行百分号编码之后,在每个已定义对之间将sep字符串附加到结果.
- 如果named为true,则对于每个已定义的列表成员或具有定义值的数组(name, value)对,执行:
当此表达式的变量列表用尽时,返回扫描模板的其余部分.