1:插值
概览
插值的使用格式是: ${expression}
,这里的 expression
可以是所有种类的表达式(比如 ${100 + x}
)。
插值是用来给 表达式
插入具体值然后转换为文本(字符串)。插值仅仅可以在两种位置使用:在 文本 区 (比如 <h1>Hello ${name}!</h1>
) 和 字符串表达式 (比如 <#include "/footer/${company}.html">
)中。
表达式的结果必须是字符串,数字或者日期/时间/日期-时间值, 因为(默认是这样)仅仅这些值可以被插值自动转换为字符串。其它类型的值 (比如布尔值,序列)必须 "手动地" 转换成字符串(后续会有一些建议), 否则就会发生错误,中止模板执行。
一个常犯的错误是在不能使用插值的地方使用了它。 插值 仅仅 在 文本 区 (比如 <h1>Hello ${name}!</h1>
) 和 字符串 (比如 <#include "/footer/${company}.html">
)中起作用。 典型的 错误 使用是 <#if ${big}>...</#if>
,这是语法上的错误。 只要简单写为 <#if big>...</#if>
即可。 而且 <#if "${big}">...</#if>
也是 错误的,因为这样参数就是字符串类型了, 但是 if
指令的参数要求是布尔值,所以就会发生运行时错误。
字符串插入指南:不要忘了转义!
如果插值在 文本 区 (也就是说,不在 字符串表达式 中),如果 escape
指令 起作用了,那么将被插入的字符串会被自动转义。如果要生成HTML, 那么强烈建议你利用它来阻止跨站脚本攻击和非格式良好的HTML页面。这里有一个示例:
<#escape x as x?html> ... <p>Title: ${book.title}</p> <p>Description: <#noescape>${book.description}</#noescape></p> <h2>Comments:</h2> <#list comments as comment> <div class="comment"> ${comment} </div> </#list> ... </#escape>
这个示例展示了当生成HTML时,最好将完整的模板放入到 escape
指令中。那么,如果 book.title
包含 &
, 在输出中它就会被替换成&
, 而页面还会保持为格式良好的HTML。如果用户注释包含如 <iframe>
(或其它元素)的标记,那么就会被转义成 <iframe>
的样子,使他们没有任何有害点。 但有时在数据模型中真的需要HTML,我们假设上面的 book.description
在数据库中的存储是HTML格式的, 那么此时不得不使用 noescape
来抵消 escape
的转义,不包含 escape
, 模板就会像这样了:
... <p>Title: ${book.title?html}</p> <p>Description: ${book.description}</p> <h2>Comments:</h2> <#list comments as comment> <div class="comment"> ${comment?html} </div> </#list> ...
这和之前示例的效果是一样的, 但是这里可能会忘记 ?html
等内建函数,那么这就会有安全上的问题了。 在之前的示例中,你可能忘记noescape
等内建函数, 也会造成不良的输出,但是起码是没有安全隐患的。
数字插入指南
如果表达式是数字类型,那么根据数字的默认格式, 数值将会转换成字符串。这也许会包含最大的小数, 数字分组和相似处理的问题。通常程序员应该设置默认的数字格式; 而模板设计者不需要处理它(但是可以使用 number_format
来设置;详情请参考 setting
指令部分的文档)。 可以使用 内建函数 string
为一个插值来重写默认数值格式。
小数的分隔符通常(其他类似的符号也是这样,如分组符号) 是根据所在地的标准(语言,国家)来确定的,这也需要程序员来设置。例如这个模板:
${1.5}
如果当前本地化设置为英语时,将会输出:
1.5
而当前地区为德国时,将会输出:
1,5
这是因为德国人使用逗号作为小数点。
可以看出,插值的打印都是给用户看的(至少是这样的), 而不是给''计算机''的。有时候这样并不好,比如要打印数据库记录的主键, 用来作为URL中的一部分或HTML表单的隐藏域来作为提交内容, 或者要打印CSS/JavaScript中的数字,因为这些值都是给计算机程序去识别的而不是给用户看的。 很多程序对数字格式的要求非常严格,它们只能理解一部分简单的美式数字格式。那样的话, 可以使用内建函数c
(代表''计算机'')来解决这个问题,比如:
<a href="/shop/productdetails?id=${product.id?c}">Details...</a>
日期/时间插入指南
如果表达式的值是时间日期类型,那么日期中的数字将会按照默认格式来转换成文本。 通常程序员应该设置默认格式,而页面设计者无需处理这一点。(如果需要的话, 可以参考 date_format
, time_format
和 datetime_format
的 setting
指令设置)。当然,也可以使用 内建函数string
来覆盖单独插值的默认格式。
为了将日期显示成文本,FreeMarker 必须知道日期中的哪一部分在使用,也就是说, 如果仅仅日期部分(年,月,日)使用或仅仅时间部分(时,分,秒,毫秒)使用或两部分都用。 不幸的是,由于Java平台技术的限制,自动探测一些变量是不现实的。 这时可以找程序员对数据模型中可能出问题的变量进行处理。 如果找出时间日期变量的哪部分在使用是不太可能的话,就必须帮助 FreeMarker 使用内建函数 date
, time
和 datetime
来识别 (比如 ${lastUpdated?datetime}
),否则就会出现错误停止执行。
布尔值插入指南
若要使用插值方式来打印布尔值会引起错误,中止模板的执行。 例如: ${a == 2}
就会引起错误, 它不会打印''true''或其他内容。这是因为没有全局来表示布尔值的好方法 (有时想输出yes/no,但有时是想要enabled/disabled,on/off等等)。
我们可以使用内建函数 ?string
来将布尔值转换为字符串形式。比如输出变量"married"的值(假设它是一个布尔值), 那么可以这么来写:${married?string("yes", "no")}
。
可以使用设置参数 boolean_format
来为 FreeMarker 配置默认的布尔值格式。那么,直接编写 ${married}
这样的代码就不会有问题了。但在很多应用程序中, 这样的做法是不推荐使用的,因为布尔值在不同的地方就应该呈现出不同的格式, 同时将格式留作默认值也可以认为是疏忽,因为这可能导致错误产生。
当想生成JavaScript或其它计算机语言代码部分时,那么可以考虑使用 ${someBoolean?c}
("c" 代表计算机)来输出布尔值true/false。 (请记住?c
也可以用来输出给计算机看的数字。)
精确的转换规则
对于有兴趣研究的人,表达式的值转换为字符串(仍受限于转义) 精确的规则就是下面这些,以这个顺序进行:
-
如果这个值是数字,那么它会按照指定的
number_format
设置规则来转换为字符串。所以这些转换通常是对用户进行的,而不是对计算机。 -
如果这个值是日期,时间或时间日期类型的一种,那么它们会按照指定的
date_format
,time_format
或者datetime_format
设置规则来转换为字符串。 如果它不能被探测出来是哪种日期类型(日期或时间或日期时间)时,就会发生错误了。 -
如果值本来就是字符串类型的,不需要转换。
-
如果 FreeMarker 引擎在传统兼容模式下:
-
如果值是布尔类型,true值就转换成"true",false值将会转换为空字符串。
-
如果表达式未被定义(
null
或者变量未定义), 那么就转换为空字符串。 -
否则就会发生错误中止模板执行。
-
-
否则就会发生错误中止模板执行。