使用模板生成 URI 和 IRI
毫无疑问,统一资源标识符(URI)是基于 Web 的应用程序的最重要特征之一。URI 提供了一种简单、一致且持久的方式,用于标识和查找网络上可能存在的资源。
过去,URI 曾经被隐藏在 Web 浏览器界面和表单之后。它们通常被当作不透明的标记处理,与浏览器相比,URI 的内部结构和数据对于服务器来说更重要。在这种情况下,应用程序通常只创建供自己使用的 URI。但是,在某些情况下,应用程序需要允许其他系统构建将要使用的 URI。
例如,基于 Web 的服务可以提供一种方式,允许客户机请求地球上给定经纬度坐标的位置的当前天气状况。每个坐标由类似 http://geoweather.example.org?long=36.330892&lat=-119.654846 这样的 URI 指定。显然,从设计上来说,由服务器定义此服务的每个可能的 URI 是不切实际的。相反,此服务需要一种描述 URI 模式的方式,允许客户机应用程序构造被访问资源的正确 URI。
URI 模板规范提出了一种语法,可用于描述如何构造有意义的 URI。
URI 模板
URI 模板是一个字符串值,由文字字符序列和特殊的标记组成,可以将一系列输入值转换为一个 URI。例如:
清单 1. 示例 URI 模板
http://{-append|.|host}example.org{-opt|/|segments}{-listjoin|/|segments} /page/{id=~home}{-opt|?|a,b,c}{-join|&|a,b,c} |
模板语法不但易于处理,而且易于理解。看一下此示例,只要熟悉基本的 URI 语法,就能够快速理解模板生成的 URI。
为了处理模板,我首先将其分解为各个组件 —— 本质上是由花括号 “{” 和 “}”(ASCII 码值为 0x7B 和 0x7D)分开的文字值和标记组成的数组。文字值可以直接复制到生成的 URI 中,而标记需要进行处理,以确定一个合适的替换值。清单 2 展示了被分解为不同组件的示例 URI 模板:
清单 2. 示例 URI 模板的不同组件
[http://] [{-append|.|host}] [example.org] [{-opt|/|segments}] [{-listjoin|/|segments}] [/page/] [{id=~home}] [{-opt|?|a,b,c}] [{-join|&|a,b,c}] |
此示例中有 6 个不同的标记。清单 3 展示了示例 URI 模板中出现的标记,按出现的顺序排列:
清单 3. 示例 URI 模板中出现的标记
{-append|.|host} {-opt|/|segments} {-listjoin|/|segments} {id=~home} {-opt|?|a,b,c} {-join|&|a,b,c} |
清单 4 展示了模板标记可以采用的 3 种基本形式:
清单 4. URI 模板标记的基本形式
{variable} {variable=default} {-operation|arg|variable(s)} |
前后的花括号用于将模板中的文字值和标记区分开。
使用 {variable}
或 {variable=default}
形式的标记表示一个简单的替换操作。也就是说,该标记将会被与变量名关联的值替换;如果没有定义关联值,那么就会替换为默认值或空字符串。例如,如果有一个模板 http://example.org/{foo=baz}
,而且应用程序已将值bar
与变量 foo
关联,则标记 {foo}
将会被替换为 bar
,因此产生的结果为 http://example.org/bar
。如果没有对变量 foo
赋值,则将会选择默认值 baz
,从而生成的结果为 http://example.org/baz
。如果没有指定默认值,则标记将会被替换为空字符串,从而生成的结果为 http://example.org/
。
{-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 模板变量关联的值
变量 | 值 | 描述 |
---|---|---|
host | www |
字符串值 www
|
segments | ["foo","bar","baz"] |
三个字符串值 foo 、bar 和 baz 的列表
|
id | 未定义 | |
a | x |
字符串值 x
|
b | y |
字符串值 y
|
c | z |
字符串值 z
|
给定这些值,可以计算出 URI 模板的每个组件的替换值,如表 2 所示:
Table 2. 计算示例 URI 模板的替换值
文字值 | 标记 | 描述 | 替换值 |
---|---|---|---|
http://
|
插入文字值 http://
|
http:// | |
{-append|.|host}
|
插入值 www 并附加上文字值 . (ASCII 码值为 0x2E)
|
www.
|
|
example.org
|
插入文字值 example.org
|
example.org
|
|
{-opt|/|segments}
|
由于已经定义了 segments 的一个值,所以插入文字值 / (ASCII 码值为 0x2F)
|
/
|
|
{-listjoin|/|segments}
|
使用文字值 / (ASCII 码值为 0x2F)将值 foo 、bar 、baz 连接起来
|
foo/bar/baz
|
|
/page/
|
插入文字值 /page/
|
/page/
|
|
{id=~home}
|
插入默认的文字值 ~home
|
~home
|
|
{-opt|?|a,b,c}
|
由于已经定义了变量 a 、b 和 c 的值,插入文字值 ? (ASCII 码值为 0x3F)
|
? | |
{-join|&|a,b,c}
|
使用文字值 & (ASCII 码值为 0x26)将 a=x 、b=y 和 c=z 连接起来
|
a=x&b=y&c=z |
确定每一个替换值之后,得到的 URI 模板如下所示:
清单 5. 重新组装扩展后的 URI 模板的组件
[http://] [www.] [example.org] [/] [foo/bar/baz] [/page/] [~home] [?] [a=x&b=y&c=z] |
当各个组件被重新组合起来时,形成如下所示的完整 URI:
清单 6. 生成的 URI
http://www.example.org/foo/bar/baz/page/~home?a=x&b=y&c=z |
在各种上下文和应用程序中,URI 都模板很有用。基于此原因,在尽可能多的编程语言中拥有多个可用实现非常重要。在后面的章节中,我将演示针对 Apache Abdera 项目集成的 JavaScript 和 Java 实现。
![]() ![]() |
![]()
|
JavaScript 中的 URI 模板
http://www.snellspace.com/public/template.js 上的 URI 模板 JavaScript 库提供了当前 URI 模板规范的完整实现。这个库使用起来很简单,导入这个库并创建模板类的实例即可,如清单 7 所示:
清单 7. 使用 JavaScript URI 模板实现
<html> <head> <script src="template.js"></script> </head> <body> <script> var template = new Template("http://example.org/{foo.bar.baz}"); var context = { "foo" : { "bar" : { "baz" : "t/u00e9st" } } } ; document.write( template.expand( context ) ); </script> </body> |
清单 7 中代码的输出 URI 是 http://example.org/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 模板实现
Template template = new Template("http://example.org/{foo}/{bar}/{baz}"); Map map = new HashMap(); map.put("foo","a"); map.put("bar","b"); map.put("baz","c"); System.out.println(template.expand(map)); |
清单 9 中演示的上下文接口允许应用程序提供模板变量替换值的定制解决方案。
清单 9. 使用定制上下文接口实现提供变量替换值
final Template template = new Template("http://example.org/{foo}/{bar}/{baz}"); Context context = new CachingContext() { protected <T> T resolveActual(String var) { return (T) ( var.equals("foo") ? "a" : var.equals("bar") ? "b" : var.equals("baz") ? "c" : null ); } public Iterator<String> iterator() { return template.iterator(); } }; System.out.println(template.expand(context)); |
开发人员也可以使用普通 Java 对象的 public 字段和 getter 方法提供用于模板扩展的值,如清单 10 所示:
清单 10. 使用 Java 对象提供变量替换值
public class MyObject { public String getFoo() { return "a"; } public String getBar() { return "b"; } public String getBaz() { return "c"; } } Template template = new Template("http://example.org/{foo}/{bar}/{baz}"); MyObject myobj = new MyObject(); System.out.println(template.expand(myobj)); |
在许多情形中,当开发人员使用 Java 对象提供替换值时,可以方便地将模板直接关联到正在使用的对象类。基于此目的,Abdera 实现提供了两种注释对象(JDK 1.5 或更高版本)。
@URITemplate
注释将一个模板与一个 Java 类关联,如清单 11 所示:
清单 11. 使用 @URITemplate 注释
@URITemplate("http://example.org/{foo}/{bar}/{baz}") public static class MyObject { public String getFoo() { return "a"; } public String getBar() { return "b"; } public String getBaz() { return "c"; } } |
然后可以使用静态模板类 expandAnnotated
方法将类实例扩展为 URI,如清单 12 所示:
清单 12. 使用 expandAnnotated 方法
MyObject myobj = new MyObject(); System.out.println(Template.expandAnnotated(myobj)); |
默认情况下,模板处理程序自动尝试将模板变量名称映射到 Java 类上的 public 字段和 getter 方法。例如,foo
变量被映射到 foo
字段或 foo
或 getFoo
方法。映射区分大小写,只有当方法拥有一个返回值而且没有输入参数时才能匹配。
对于大多数应用程序,默认映射远远不够。但是,在一些情形中,字段或方法的名称与模板变量名称并不匹配。要处理这种情形,可以使用 @VarName
属性关联可选的变量名映射,如清单 13 所示:
清单 13. 使用 @VarName 属性指定可选的变量名映射
@URITemplate("http://example.org/{foo}/{bar}/{baz}") public static class MyObject { public String getFoo() { return "a"; } public String getBar() { return "b"; } @VarName("baz") public String getSomething() { return "c"; } } |
![]() ![]() |
![]()
|
国际化模板
根据当前规范草案的定义,模板生成的 URI 只能包含 RFC 3986(URI 规范)允许的 US ASCII 字符集中的字符(参见 参考资料)。 然而,对于许多应用程序来说,支持扩展字符集的能力非常重要。为了满足这些应用程序的需要,Java 和 JavaScript 实现都支持 “国际化模板” 的概念,可以生成 RFC 3987 中定义的 IRI(参见 参考资料)。
除了变量名称可以包含 IRI 规范 iunreserved
部分允许的任何字符以外,国际化模板与 URI 模板大体相同。它可以包含双向字符,而且可用于生成 IRI。以下示例展示了一个包含 Greek 变量名称和文字组件的国际化模板,可用于生成 IRI:
清单 14. 包含希腊字母表中的字符和文字组件的国际化模板
http://παραδειγμα.org/{ονομα}{-listjoin|/|τμηματα} http://παραδειγμα.org/ευρωπη/α/β/γ |
在模板中使用非 ASCII 字符的最困难和容易混淆的方面是对双向语言的支持,比如 Hebrew 和 Arabic。当字符串由从左到右和从右到左的字符组成时,呈现这种字符串就会变得很复杂且容易混淆。基于此原因,对于实现者来说,在构造双向模板时采用一个简单的规则集尤为重要。
第一个规则是,国际化模板必须按照逻辑顺序进行存储和传递。换句话说,无论模板中包含的字符将会按哪种顺序显示,字符串的逻辑顺序总是从左到右。
但是用于显示时,国际化模板应该按从左到右的顺序呈现,就好像在其前面添加 Unicode 双向格式字符 U+202D,而在其后面添加 U+202C 一样。另一方面,模板变量名称必须以从左到右的嵌入方式呈现;即,相当于在其前面添加 Unicode 双向格式字符 U+202A,在后面添加 U+202C。这些规则确保在显示时,国际化模板能够正确且一致地呈现,而且仍然能够自然地读取变量名。
以下示例说明了国际化模板中字符的逻辑和表示顺序的区别。大写字母表示从右到左的字符。
清单 15. 国际化模板中字符的逻辑顺序
http://example.org/{-prefix|XYZ|ABCD} |
清单 16. 国际化模板中字符的表示顺序
http://example.org/{-prefix|XYZ|DCBA} |
国际化模板可以直接包含任何 Unicode 双向格式字符,从而确保正确呈现模板,但是模板处理程序将会在处理之前从模板移除这些字符。模板处理程序产生的 URI 和 IRI 不包含任何双向格式代码。
![]() ![]() |
![]()
|
结束语
URI 模板规范是一个不断演化的项目。希望本文使您基本理解了 URI 模板语法和将模板扩展为 URI 的方法。您应该了解了 JavaScript 和 Java 语言程序的 URI 模板实现,而且熟悉了与生成 IRI 相关的一些概念。如果阅读本文后对 URI 模板规范有任何疑问和评论,请将反馈和讨论发送到 http://lists.w3.org/Archives/Public/uri/ 上的 W3C URI 邮件列表。将与 JavaScript 和 Java URI 模板实现相关的反馈和讨论发送到 http://incubator.apache.org/abdera/project.html#lists 上的 Apache Abdera 开发人员邮件列表。
使用模板生成 URI 和 IRI相关推荐
- 利用Freemarker模板生成doc或者docx文档(转载整理)
可以直接看主要代码实现 doc作为模板文件生成指定格式的doc文件 实现逻辑 1.把作为模板的doc文件另存为xml文件 2.凡是需要填充的数据用${xxxx}替代 3.利用Template类将数据填 ...
- Android 使用模板生成Word文档,支持手机直接查看word
最近在项目工作中,碰到一个很棘手的需求,说是要在手机端根据模板生成word文档,而且不借助第三方的软件可以查看word文档,一开始听这个需求差不多蒙了,这要怎么做,为什么不把生成word文档这个工作放 ...
- 使用POI根据合同定义模板生成新的模板并且填充数据(包括图片)
word文档中,需要填充数据的地方统一使用变量的形式,格式如下:${变量名}. 注意:变量"${变量名}"建议先在记事本中写好,再粘贴到"XXX合同.docx" ...
- python excel处理框架_django框架基于模板 生成 excel(xls) 文件操作示例
本文实例讲述了django框架基于模板 生成 excel(xls) 文件操作.分享给大家供大家参考,具体如下: 生成Excel 文件,很多人会采用一些开源的库来实现,比如python 自带 csv 库 ...
- 从零开始的webpack生活-0x003:html模板生成
0x001 概述 上一章之后已经可以自动刷新浏览器了,大大方便了我们的开发,这章开始讲插件,第一个插件将会解决上一章节的一个问题,那就是index.html需要手动插入打包好的js,同时不会将inde ...
- 创建代码生成器可以很简单:如何通过T4模板生成代码?[下篇]
在<上篇>中我们通过T4模板为我们指定的数据表成功生成了我们需要的用于添加.修改和删除操作的存储过程.但是这是一种基于单个文件的解决方案,即我们必须为每一个生成的存储过程建立一个模板.如果 ...
- [转]MVC实用架构设计(三)——EF-Code First(3):使用T4模板生成相似代码
本文转自:http://www.cnblogs.com/guomingfeng/p/mvc-ef-t4.html 〇.目录 一.前言 二.工具准备 三.T4代码生成预热 (一) 单文件生成:Hello ...
- POI 使用替换字符方式进行模板生成word
1.Word生成 package com.tepper.common.util;import java.util.Iterator; import java.util.List; import jav ...
- NetTier模板生成的代码框架用法 (转)
1.1. 概述: 使用NetTier模板生成的.net代码,包括完整的数据层,使用的技术是微软的 EnterpriseLibrary1.1版本,对应每张表都生成相对应的增删改查函数和存储过程,在查询中 ...
最新文章
- 模型与logit_互助问答第33期:条件logit模型相关问题
- 每日一皮:不允许穿格子衫之后...
- 自编码器Autoencoders
- Python collection模块
- Linux内核分析 02
- python/匿名函数和内置函数
- java 执行class顺序_java – @BeforeClass和inheritance – 执行顺序
- 【Kafka】Kafka 修改某个消费组的偏移量
- 初学者python笔记(文件的操作)
- oc转java_OC和Java
- 可展开/折叠的Android TextView:ExpandableTextView
- box-shadow属性四个值_CSS常见属性和值
- 为什么重复率高的字段不适合作为索引
- 505错误:Invalid bound statement (not found): com.pc.dao.BookMapper.queryAllBook错误解决
- mysql查询提示_MySQL自成一派的查询提示
- Python下openCV打开图片的几种方式/适应窗口大小
- c语言程序设计分值,计算机考试题分值分布
- 国内哪家的香港云服务器哪家好?
- Pro/E Wildfire3.0二次开发环境配置
- Excel工作表的移动