企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
## 1.15 安全输出(重要) [TOC] 安全输出是任何一个模板引擎必须重视的问题,否则,将极大困扰模板开发者。Beetl中,如果要输出的模板变量为null,则beetl将不做输出,这点不同于JSP,JSP输出null,也不同于Freemarker,如果没有用`!`,它会报错。 模板中还有俩种情况会导致模板输出异常 - 有时候模板变量并不存在,这时候必须报错,如果简单忽略不输出(Velocity就是这样),很容易留坑 - 模板变量为null,但输出的是此变量的一个属性,如 `${user.wife.name}` 针对前两种情况,可以在变量引用后加上 `!` 以提醒beetl这是一个安全输出的变量,变量确实有可能不存在 如 `${user.wife.name! }`,即使user不存在,或者user为null,或者user.wife为null,或者user.wife.name为null beetl都不将输出 可以在!后增加一个常量(字符串,数字类型等),或者另外一个变量,方法,本地调用,作为默认输出,譬如: `${user.wife.name!"单身"}`,如果user为null,或者user.wife为null,或者user.wife.name为null,输出`单身` 譬如 `${user.birthday!@System.constants.DefaultBir}`, 表示如果user为null,或者user. birthday为null,输出`System.constants.DefaultBir` 还有一种情况很少发生,但也有可能,输出模板变量发生的任何异常,如变量内部抛出的一个异常 这需要使用格式${!(变量)},这样,在变量引用发生任何异常情况下,都不作输出,譬如 `${!(user.name)}`,Beetl将会调用 `user.getName()` 方法,如果发生异常,Beetl 将会忽略此异常,继续渲染 值得注意的是,在变量后加上!不仅仅可以应用于占位符输出(但主要是应用于占位符输出),也可以用于表达式中,如: ```javascript <% var k = user.name!'N/A'+user.age!; %> <% ${k} %> ``` 如果 user 为 null ,则 k 值将为`N/A` 在有些模板里,可能整个模板都需要安全输出,也可能模板的部分需要安全输出,使用者不必为每一个表达式使用!,可以使用beetl的安全指示符号来完成安全输出 如: ```javascript <% DIRECTIVE SAFE_OUTPUT_OPEN; %> ${user.wife.name} 模板其他内容,均能安全输出…… <% //关闭安全输出。 DIRECTIVE SAFE_OUTPUT_CLOSE; %> ``` Beetl 不建议每一个页面都使用 `DIRECTIVE SAFE_OUTPUT_OPEN`,这样,如果真有不期望的错误,不容易及时发现,其次,安全输出意味着 Beetl 会有额外的代码检测值是否存在或者是否为null,性能会略差点。所以建议及时关闭安全输出(这不是必须的,但页面所有地方是安全输出,可能不容易发现错误) 如果你的所有模板都想安全输出,可以配置,但不推荐。严格了错误,就像 try-catch 吃掉异常一样不容易发现这是个错误 ``` SAFE_OUTPUT=true ``` 在 for-in 循环中 ,也可以为集合变量增加安全输出指示符号,这样,如果集合变量为null,也可以不进入循环体,如: ```javascript <% var list = null; for(item in list!){ }elsefor{ print("no data"); } %> ``` ### 1.15.1 变量是否存在 ```javascript <% if(has(flag)){ print("flag变量存在,可以访问") } %> ``` 如果需要判断变量是否存在,如果存在,还有其他判断条件,通常都这么写 ```javascript <% if(has(flag)&&flag==0){ //code } %> ``` 如果flag存在,而且值是0,都将执行if语句 但是,有更为简便的方法是直接用安全输出,如 ```javascript <% if(flag!0==0){ //code } %> ``` flag!0 取值是这样的,如果flag不存在,则为0,如果存在,则取值flag的值,类似三元表达式 if((has(flag)?flag:0)==0) ### 1.15.2 安全输出表达式 安全输出表达式可以包括 - 字符串常量,如 ${user.count!"无结果"} - boolean常量 ${user.count!false} - 数字常量,仅限于正数,因为如果是负数,则类似减号,容易误用,因此,如果需要表示负数,请用括号,如${user.count!(-1)} - class直接调用,如${user.count!@User.DEFAULT_NUM} - 方法调用,如 ${user.count!getDefault() } - 属性引用,如 ${user.count!user.maxCount } - 任何表达式,需要用括号