On this page
Error handling
Page Contents
可能的 exception
关于 FreeMarker 可能发生的异常可以如下分类:
配置 FreeMarker 时会发生异常:通常,在应用程序初始化时,仅在应用程序中配置一次 FreeMarker。当然,在此期间,可能会发生异常。
加载和解析模板时发生异常:调用
Configuration.getTemplate(...)
时,FreeMarker 必须将模板加载到内存中并进行解析(除非模板已经在该Configuration
对象中为cached)。在此期间,可能会发生以下几种异常:TemplateNotFoundException
,因为请求的模板不存在。请注意,这扩展了IOException
。freemarker.core.ParseException
,因为根据 FTL 语言的规则,该模板在语法上不正确。请注意,当您获得Template
对象(Configuration.getTemplate(...)
)时发生此错误,而不是在执行模板(Template.process(...)
)时发生。 。请注意,这扩展了IOException
(旧版)。任何其他类型的
IOException
,因为在读取现有模板时发生了错误。例如,您无权读取文件,或者读取模板的连接断开。这些的 Launcher 是TemplateLoader object,它已插入Configuration
对象。
执行(处理)模板时(即调用
Template.process(...)
时)发生异常。可能发生两种异常:IOException
,因为尝试写入输出写入器时发生错误。
自定义有关 TemplatException -s 的行为
模板处理期间引发的TemplateException
-s 由freemarker.template.TemplateExceptionHandler
对象处理,该对象通过setTemplateExceptionHandler(...)
方法插入到Configuration
对象中。这些是 FreeMarker 随附的TemplateExceptionHandler
实现:
TemplateExceptionHandler.DEBUG_HANDLER
:打印堆栈跟踪(包括 FTL 错误消息和 FTL 堆栈跟踪)并重新引发异常。这是默认处理程序,但是,请注意不要在生产环境中使用它,因为它会显示有关系统的技术信息。TemplateExceptionHandler.HTML_DEBUG_HANDLER
:与DEBUG_HANDLER
相同,但它格式化堆栈跟踪,以便 Web 浏览器可以读取。在生成 HTML 页面时,建议不要在DEBUG_HANDLER
上推荐,但仅应将其用于开发,因为它会显示有关系统的技术信息。TemplateExceptionHandler.IGNORE_HANDLER
:仅禁止显示所有异常(尽管Configuration.getLogTemplateExceptions
为true
时 FreeMarker 仍会记录它们)。它不处理任何事件。它不会重新引发异常。TemplateExceptionHandler.RETHROW_HANDLER
:只需重新抛出所有异常即可;它什么也没做。今天应在大多数应用程序中使用它。它不会在错误输出中输出任何有关错误的信息,这使其很安全,并且开发人员仍可以从日志中获取错误详细信息。在模板开发过程中,它不如HTML_DEBUG_HANDLER
或DEBUG_HANDLER
那样方便。有关处理 Web 应用程序请参阅常见问题中的错误的更多信息。
您也可以通过实现包含此方法的接口来编写自定义TemplateExceptionHandler
:
void handleTemplateException(TemplateException te, Environment env, Writer out)
throws TemplateException;
每当出现TemplateException
时,都会调用此方法。处理的异常在te
参数中,模板处理的运行时环境在env
参数中,并且处理程序可以使用out
参数打印到输出。如果此方法引发异常(通常会重新引发te
),则模板处理将被中止,而Template.process(...)
将引发相同的异常。如果handleTemplateException
没有引发异常,则模板处理将 continue 进行,就好像什么都没有发生一样,但是导致异常的语句将被跳过(请参阅稍后)。当然,处理程序仍可以将错误指示符打印到输出。
让我们通过示例看看在错误处理程序未引发异常时 FreeMarker 如何跳过语句。假设我们正在使用此模板异常处理程序:
class MyTemplateExceptionHandler implements TemplateExceptionHandler {
public void handleTemplateException(TemplateException te, Environment env, java.io.Writer out)
throws TemplateException {
try {
out.write("[ERROR: " + te.getMessage() + "]");
} catch (IOException e) {
throw new TemplateException("Failed to print error message. Cause: " + e, env);
}
}
}
...
cfg.setTemplateExceptionHandler(new MyTemplateExceptionHandler());
如果不在 FTL 标签内部(即未包含在<#...>
或<@...>
内)的插值中发生错误,则将跳过整个插值。因此,此模板(假设数据模型中缺少badVar
):
a${badVar}b
如果我们使用MyTemplateExceptionHandler
将打印此内容:
a[ERROR: Expression badVar is undefined on line 1, column 4 in test.ftl.]b
该模板将打印相同的内容(除了列号不同...):
a${"moo" + badVar}b
因为如果内部插值发生错误,整个插值将被跳过。
如果在评估指令调用的参数值时发生错误,或者参数列表存在其他问题,或者在<@exp ...>
中评估exp
时发生错误,或者exp
的值不是用户定义的指令,然后跳过整个指令调用。例如:
a<#if badVar>Foo</#if>b
将打印此:
a[ERROR: Expression badVar is undefined on line 1, column 7 in test.ftlh.]b
请注意,该错误发生在if
开始标记(<#if badVar>
)中,但是整个指令调用都被跳过。从逻辑上讲,嵌套内容(Foo
)被跳过了,因为嵌套内容是由封闭指令(if
)处理(打印)的。
输出与此相同(除了列号不同...):
a<#if "foo${badVar}" == "foobar">Foo</#if>b
因为如果在参数评估期间发生任何错误,将跳过整个指令调用。
如果已经开始执行指令后发生错误,则不会跳过指令调用。也就是说,如果嵌套内容中发生错误:
a
<#if true>
Foo
${badVar}
Bar
</#if>
c
或在宏定义主体中:
a
<@test />
b
<#macro test>
Foo
${badVar}
Bar
</#macro>
输出将是这样的:
a
Foo
[ERROR: Expression badVar is undefined on line 4, column 5 in test.ftlh.]
Bar
c
TemplateException logging
默认情况下,freemarker.runtime
日志类别下的 FreeMarker logs全部TemplateException
-s,即使它会从其公共 API 抛出它。由于日志记录已成为 Java 应用程序中的普遍做法,因此现在通常会导致两次 exception 记录,因此建议您在配置 FreeMarker 的cfg.setLogTemplateExceptions(false)
(或log_template_exceptions=false
)禁用此旧行为。
模板中的显式错误处理
尽管它与 FreeMarker 配置无关(本章的主题),但是出于完整性考虑,这里提到您也可以直接在模板内部处理错误:
处理缺失/空变量:模板作者指南/模板/表达式/处理缺失值
替换失败但消耗大的网页板块:模板语言参考/指令参考/尝试,恢复