Apache Camel+Springboot+电子邮件集成。 我正在尝试渲染一个 ftl 模板作为对发送到我的后端聊天机器人的电子邮件查询的回复。但是,我的 .ftl 文件无法渲染,并出现如下错误。我是 FTL 新手。任何我出错的地方或如何解决这个问题的建议都将非常好,非常感谢!
reemarker.core.InvalidReferenceException: The following has evaluated to null or missing:
==> ftlDataMap [in template "MyFtl.ftl" at line 61, column 14]
----
Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??
FTL stack trace ("~" means nesting-related):
- Failed at: #if ftlDataMap.replyMessages?size [in template "MyFtl.ftl" at line 61, column 9]
MyFtl.ftl:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Email Reply</title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
margin: 0;
padding: 0;
background-color: #f4f4f4;
}
.email-container {
width: 100%;
max-width: 800px;
margin: 20px auto;
padding: 20px;
border: 1px solid #ddd;
border-radius: 5px;
background-color: #fff;
}
.title-ribbon {
background-color: #007bff;
color: #fff;
padding: 10px;
border-radius: 5px 5px 0 0;
font-size: 20px;
font-weight: bold;
text-align: center;
}
.reply-section {
margin: 20px 0;
}
.reply-section p {
margin: 10px 0;
}
.content-html {
background-color: #f9f9f9;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
word-wrap: break-word;
}
.original-message {
border-top: 1px solid #ddd;
padding-top: 20px;
}
.separator {
margin: 20px 0;
border-top: 2px dashed #ddd;
}
</style>
</head>
<body>
<div class="email-container">
<div class="title-ribbon">
Email Reply
</div>
<#if ftlDataMap.replyMessages?size > 0>
<#list ftlDataMap.replyMessages as reply>
<#if reply.replyMessage?size > 0>
<#list reply.replyMessage?reverse as item>
<#if item.type == "AI Reply">
<p><strong>AI Reply:</strong> ${item.value}</p>
<#else>
<div class="content-html">${item.value?html}</div>
</#if>
</#list>
<#else>
<p>No replies found.</p>
</#if>
</#list>
<#else>
<p>No reply messages found.</p>
</#if>
<div class="separator"></div>
<div class="original-message">
<p><strong>-----Original Message-----</strong></p>
<#if ftlDataMap.replyMessages?size > 0>
<#list ftlDataMap.replyMessages[0].originalMessage as item>
<p><strong>${item.label}</strong> ${item.value}</p>
</#list>
<#else>
<p>No original message content found.</p>
</#if>
</div>
</div>
</body>
</html>
我的Java课程:
public class CamelIMAPProcessor implements Processor {
private static final Logger LOG = LoggerFactory.getLogger(CamelIMAPProcessor.class);
@Override
public void process(Exchange exchange) throws Exception {
Map<String, Object> ftlDataMap = new LinkedHashMap<>();
List<ValueExampleObject> originalMessage = new ArrayList<>();
Map<String, Object> reply = new LinkedHashMap<>();
List<Object> previousResp = new ArrayList<>();
List<ValueExampleObject> chatBotReply = new ArrayList<>();
String backendResponse = exchange.getIn().getBody(String.class);
String originalEmailBody = (String) exchange.getProperty("originalEmailBody");
String user_message = "";
String fromAddress = exchange.getProperty("From", String.class);
String toAddress = exchange.getIn().getHeader("To", String.class);
String subject = exchange.getIn().getHeader("Subject", String.class);
String timeStamp = exchange.getIn().getHeader("Date", String.class);
ObjectMapper objectMapper = new ObjectMapper();
JsonNode rootNode = objectMapper.readTree(backendResponse);
JsonNode groupInfo = rootNode.path("group_info");
JsonNode conversationObjects = groupInfo.path("conversation_objects");
// Extract "chat-text" and "html-embed" objects
for (JsonNode objectNode : conversationObjects) {
String objectType = objectNode.path("object_type").asText();
JsonNode objectContent = objectNode.path("object_content");
if ("chat-text".equals(objectType)) {
LOG.info("Inside chat-text....");
String myBackendResponseResponse = objectContent.path("response").path("textData").asText();
user_message = objectContent.path("query").asText();
chatBotReply.add(new ValueExampleObject("AI Reply", myBackendResponseResponse));
}
if ("html-embed".equals(objectType)) {
LOG.info("Inside html-embed-response....");
String htmlContent = objectContent.path("content").asText();
chatBotReply.add(new ValueExampleObject("Embedded Content", htmlContent));
}
}
reply.put("replyMessage", chatBotReply);
originalMessage.add(new ValueExampleObject("-----Original Message-----", ""));
originalMessage.add(new ValueExampleObject("From: ", fromAddress));
originalMessage.add(new ValueExampleObject("Sent: ", timeStamp != null ? timeStamp : ""));
originalMessage.add(new ValueExampleObject("To: ", toAddress));
originalMessage.add(new ValueExampleObject("Subject: ", subject != null ? subject : ""));
originalMessage.add(new ValueExampleObject("User Query:", user_message));
originalMessage.add(new ValueExampleObject("", ""));
reply.put("originalMessage", originalMessage);
if (!reply.isEmpty()) {
previousResp.add(reply);
ftlDataMap.put("replyMessages", previousResp);
}
String output = createHtmlFromFreeMarkerTemplate(ftlDataMap);
exchange.getIn().setBody(output);
}
private String createHtmlFromFreeMarkerTemplate(Map<String, Object> obj {
try {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_28);
cfg.setClassForTemplateLoading(CamelIMAPProcessor.class, "/templates");
cfg.setDefaultEncoding("UTF-8");
cfg.setLocale(Locale.US);
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
Template template = cfg.getTemplate("MyFtl.ftl");
StringWriter writer = new StringWriter();
template.process(obj, writer);
return writer.toString();
} catch (Exception e) {
// handle exc
}
}
}
您有这个 Java 局部变量
Map<String, Object> ftlDataMap
,但它的名称对所有内容都是透明的,因为它只是一个局部变量。所以你不能在模板中引用它。无论如何,您正在使用 Map
数据模型的根,因此 Map
中的键是模板中的顶级变量名称。因此,您应该写 ftlDataMap.replyMessages
,而不是 replyMessages
。
另外
<#if ftlDataMap.replyMessages?size > 0>
也不正确,>
中的 > 0
将关闭标签。请使用 <#if ftlDataMap.replyMessages?size != 0>
代替。 (当你确实需要使用 >
运算符时,如果由于某种原因它尚未在括号内,则必须改为编写 gt
。)