毫无疑问,统一资源标识符(URI)是基于 Web 的应用程序的最重要特征之一。URI 提供了一种简单、一致且持久的方式,用于标识和查找网络上可能存在的资源。
过去,URI 曾经被隐藏在 Web 浏览器界面和表单之后。它们通常被当作不透明的标记处理,与浏览器相比,URI 的内部结构和数据对于服务器来说更重要。在这种情况下,应用程序通常只创建供自己使用的 URI。但是,在某些情况下,应用程序需要允许其他系统构建将要使用的 URI。
例如,基于 Web 的服务可以提供一种方式,允许客户机请求地球上给定经纬度坐标的位置的当前天气状况。每个坐标由类似 ?long=36.330892&lat=-119.654846 这样的 URI 指定。显然,从设计上来说,由服务器定义此服务的每个可能的 URI 是不切实际的。相反,此服务需要一种描述 URI 模式的方式,允许客户机应用程序构造被访问资源的正确 URI。
URI 模板规范提出了一种语法,可用于描述如何构造有意义的 URI。
URI 模板
URI 模板是一个字符串值,由文字字符序列和特殊的标记组成,可以将一系列输入值转换为一个 URI。例如:
清单 1. 示例 URI 模板
模板语法不但易于处理,而且易于理解。看一下此示例,只要熟悉基本的 URI 语法,就能够快速理解模板生成的 URI。
为了处理模板,我首先将其分解为各个组件 —— 本质上是由花括号 “{” 和 “}”(ASCII 码值为 0x7B 和 0x7D)分开的文字值和标记组成的数组。文字值可以直接复制到生成的 URI 中,而标记需要进行处理,以确定一个合适的替换值。清单 2 展示了被分解为不同组件的示例 URI 模板:
清单 2. 示例 URI 模板的不同组件
此示例中有 6 个不同的标记。清单 3 展示了示例 URI 模板中出现的标记,按出现的顺序排列:
清单 3. 示例 URI 模板中出现的标记
清单 4 展示了模板标记可以采用的 3 种基本形式:
清单 4. URI 模板标记的基本形式
前后的花括号用于将模板中的文字值和标记区分开。
使用{variable}
或{variable=default}
形式的标记表示一个简单的替换操作。也就是说,该标记将会被与变量名关联的值替换;如果没有定义关联值,那么就会替换为默认值或空字符串。例如,如果有一个模板/{foo=baz}
,而且应用程序已将值bar
与变量foo
关联,则标记{foo}
将会被替换为bar
,因此产生的结果为/bar
。如果没有对变量foo
赋值,则将会选择默认值baz
,从而生成的结果为/baz
。如果没有指定默认值,则标记将会被替换为空字符串,从而生成的结果为/
。
{-operation|arg|variable(s)}
形式的标记稍微复杂一点。-operation
标识 6 种不同类型操作的其中之一,用于生成标记的替换值。arg
指定将根据-operation
的语法将一个文字字符序列插入到结果 URI 中。variable(s)
组件是一个或多个指定变量的列表,这些变量用作-operation
的输入。
-operation
必须是以下操作之一:
-append
—— 指示如果定义了arg
的值,其值将会附加到变量值之后。-prefix
—— 指示如果定义了arg
的值,其值将会添加到变量值前面。-opt
—— 指示只有当为任何指定变量定义了一个值时,才将arg
的值用作标记的替换值。-neg
—— 指示只有当为任何指定变量定义了一个值时,才将arg
的值用作标记的替换值。-listjoin
—— 指示指定变量的值是一个列表,将使用arg
的值将其成员连接起来。-join
—— 指示使用arg
值连接起来的多个 “变量=值” 对将用作标记的替换值。
对于-append
、-prefix
和-listjoin
操作,只能在标记中指定单个指定变量。例如,{-prefix|/|foo}
。 对于-append
和-prefix
,与指定变量关联的值必须是单个字符串值。对于-listjoin
,与指定变量关联的值可以是单个字符串值,或者是 0 个或多个字符串值列表。
对于-opt
、-neg
和-join
操作,可以在标记中列出多个指定变量。例如,{-opt|/|foo,bar,baz}
。 对于-opt
,只有为列出的所有变量定义一个值时,才能将arg
的值用作替换值。对于-neg
,只有没有为所有列出的变量定义值时,才能将arg
的值用作替换值。
-join
操作是一个特例,用于简化传统风格 URI 查询参数的生成。列出的每个变量及其关联值或默认值,都被用来生成由arg
值分开的 “名称=值” 对组成的字符串。例如,给定标记{-join|&|a,b=bar}
和与变量a
关联的一个foo
值,则标记的替换值为a=foo&b=bar
。
要更好地理解这些操作的工作原理,练习一下前面提供的示例将很有帮助。
模板中包含 6 个变量,分别是:host
、segments
、id
、a
、b
和c
。表 1 列出了与每个指定变量关联的值。
表 1. 与示例 URI 模板变量关联的值
给定这些值,可以计算出 URI 模板的每个组件的替换值,如表 2 所示:
Table 2. 计算示例 URI 模板的替换值
确定每一个替换值之后,得到的 URI 模板如下所示:
清单 5. 重新组装扩展后的 URI 模板的组件
当各个组件被重新组合起来时,形成如下所示的完整 URI:
清单 6. 生成的 URI
在各种上下文和应用程序中,URI 都模板很有用。基于此原因,在尽可能多的编程语言中拥有多个可用实现非常重要。在后面的章节中,我将演示针对 Apache Abdera 项目集成的 JavaScript 和 Java 实现。
JavaScript 中的 URI 模板
/public/template.js上的 URI 模板 JavaScript 库提供了当前 URI 模板规范的完整实现。这个库使用起来很简单,导入这个库并创建模板类的实例即可,如清单 7 所示:
清单 7. 使用 JavaScript URI 模板实现
清单 7 中代码的输出 URI 是/t%c3%a9st
。注意,模板处理程序可以根据对象属性从上下文对象提取出替换值,然后将 Unicode 字符 /u00E9(字母 e 大小写敏感)自动转换为对应的 UTF-8 百分比编码。
Java 中的 URI 模板
对于 Java 开发人员,一个 URI 模板的实现已作为 Apache Abdera 项目的一部分提供。与 JavaScript 库一样,Java 实现也提供了当前 URI 模板规范的完整实现。它还提供了一些附加功能,比如将java.util.Map
实例或普通 Java 对象作为变量替换值的来源。清单 8 演示了 Abdera URI 模板实现的基本用法:
清单 8. 使用 Abdera URI 模板实现
清单 9 中演示的上下文接口允许应用程序提供模板变量替换值的定制解决方案。
清单 9. 使用定制上下文接口实现提供变量替换值
开发人员也可以使用普通 Java 对象的 public 字段和 getter 方法提供用于模板扩展的值,如清单 10 所示:
清单 10. 使用 Java 对象提供变量替换值
在许多情形中,当开发人员使用 Java 对象提供替换值时,可以方便地将模板直接关联到正在使用的对象类。基于此目的,Abdera 实现提供了两种注释对象(JDK 1.5 或更高版本)。
@URITemplate
注释将一个模板与一个 Java 类关联,如清单 11 所示:
清单 11. 使用 @URITemplate 注释
然后可以使用静态模板类expandAnnotated
方法将类实例扩展为 URI,如清单 12 所示:
清单 12. 使用 expandAnnotated 方法
默认情况下,模板处理程序自动尝试将模板变量名称映射到 Java 类上的 public 字段和 getter 方法。例如,foo
变量被映射到foo
字段或foo
或getFoo
方法。映射区分大小写,只有当方法拥有一个返回值而且没有输入参数时才能匹配。
对于大多数应用程序,默认映射远远不够。但是,在一些情形中,字段或方法的名称与模板变量名称并不匹配。要处理这种情形,可以使用@VarName
属性关联可选的变量名映射,如清单 13 所示:
清单 13. 使用 @VarName 属性指定可选的变量名映射
国际化模板
根据当前规范草案的定义,模板生成的 URI 只能包含 RFC 3986(URI 规范)允许的 US ASCII 字符集中的字符(参见参考资料)。 然而,对于许多应用程序来说,支持扩展字符集的能力非常重要。为了满足这些应用程序的需要,Java 和 JavaScript 实现都支持 “国际化模板” 的概念,可以生成 RFC 3987 中定义的 IRI(参见参考资料)。
除了变量名称可以包含 IRI 规范iunreserved
部分允许的任何字符以外,国际化模板与 URI 模板大体相同。它可以包含双向字符,而且可用于生成 IRI。以下示例展示了一个包含 Greek 变量名称和文字组件的国际化模板,可用于生成 IRI:
清单 14. 包含希腊字母表中的字符和文字组件的国际化模板
在模板中使用非 ASCII 字符的最困难和容易混淆的方面是对双向语言的支持,比如 Hebrew 和 Arabic。当字符串由从左到右和从右到左的字符组成时,呈现这种字符串就会变得很复杂且容易混淆。基于此原因,对于实现者来说,在构造双向模板时采用一个简单的规则集尤为重要。
第一个规则是,国际化模板必须按照逻辑顺序进行存储和传递。换句话说,无论模板中包含的字符将会按哪种顺序显示,字符串的逻辑顺序总是从左到右。
但是用于显示时,国际化模板应该按从左到右的顺序呈现,就好像在其前面添加 Unicode 双向格式字符 U+202D,而在其后面添加 U+202C 一样。另一方面,模板变量名称必须以从左到右的嵌入方式呈现;即,相当于在其前面添加 Unicode 双向格式字符 U+202A,在后面添加 U+202C。这些规则确保在显示时,国际化模板能够正确且一致地呈现,而且仍然能够自然地读取变量名。
以下示例说明了国际化模板中字符的逻辑和表示顺序的区别。大写字母表示从右到左的字符。
清单 15. 国际化模板中字符的逻辑顺序
清单 16. 国际化模板中字符的表示顺序
国际化模板可以直接包含任何 Unicode 双向格式字符,从而确保正确呈现模板,但是模板处理程序将会在处理之前从模板移除这些字符。模板处理程序产生的 URI 和 IRI 不包含任何双向格式代码。
结束语
URI 模板规范是一个不断演化的项目。希望本文使您基本理解了 URI 模板语法和将模板扩展为 URI 的方法。您应该了解了 JavaScript 和 Java 语言程序的 URI 模板实现,而且熟悉了与生成 IRI 相关的一些概念。如果阅读本文后对 URI 模板规范有任何疑问和评论,请将反馈和讨论发送到/Archives/Public/uri/上的 W3C URI 邮件列表。将与 JavaScript 和 Java URI 模板实现相关的反馈和讨论发送到/abdera/project.html#lists上的 Apache Abdera 开发人员邮件列表。
如果觉得《使用模板生成 URI 和 IRI》对你有帮助,请点赞、收藏,并留下你的观点哦!