重拾cgi——页面模板

可能是因为公司里写代码用velocity习惯了,找了google的ctemplate来实现类似的功能。ctemplate相对velocity比较简单,只有变量替换,简单的引用、循环。这里有ctemplate的详细文档,相对来说,就是不能在模板上有太多的逻辑。
首先,前一篇里面贴了dispatcher,通过apache中设置环境变量,设置模板跟路径:
[cce lang="cpp"]
ctemplate::Template::SetTemplateRootDirectory(input.getenv("TEMPLATE_PATH"));
[/cce]
然后,通过绑定的处理函数,调用真正的处理uri的函数后,将输出的dictionary对象,和模板合并:
[cce lang="cpp"]
std::string templatePath;
if(context.dict) //has dict
{
//get template
templatePath = context.dict->name() + ".tpl";
//expend
std::string out;
bool expandResult = ctemplate::ExpandTemplate(templatePath, ctemplate::STRIP_WHITESPACE, context.dict.get(), &out);

if(expandResult) //good, we expend template success
{
context.ostream << cgicc::HTTPHTMLHeader();
context.ostream << out;
}
else //oops, we response 500
{
context.ostream << cgicc::HTTPStatusHeader(500, "Internal Server Error");
context.ostream << "fail to expand template: " << templatePath;
}
}
else
{
//如果没有字典,由对应的函数自己输出
}
[/cce]
这里是有字典输出的逻辑。获取根字典的名字,加上后缀,在根路径中查找对应的模板。然后调用ctemplate的函数,将模板展开。最后,将展开后的内容,通过流输出。
如果没有字典输出,默认当成处理函数中已经自己进行了输出(这里主要是为了让处理函数自己输出json内容)
另外一个分支,如果没有绑定,那么直接输出模板内容:
[cce lang="cpp"]
// find if there is a template
std::string templatePath = path.substr(1) + ".tpl";
if(ctemplate::mutable_default_template_cache()->LoadTemplate(templatePath, ctemplate::STRIP_WHITESPACE))
{
//expend
std::string out;
bool expandResult = ctemplate::ExpandTemplate(templatePath, ctemplate::STRIP_WHITESPACE,
_emptyDict.get(), &out);
if(expandResult) //good, we expend template success
{
context.ostream << cgicc::HTTPHTMLHeader();
context.ostream << out;
}
else //oops, we response 500
{
context.ostream << cgicc::HTTPStatusHeader(500, "Internal Server Error");
context.ostream << "fail to expand template: " << templatePath;
}
}
else //not bind and not find a template file
{
context.ostream << cgicc::HTTPStatusHeader(404, "not find");
context.ostream << "not find";
}
[/cce]
如果没有绑定,用一个空的字典,展开模板,也就是直接输出模板。如果这都没有找到,那么就返回404。

一个绑定函数的例子:
[cce lang="cpp"]
void handle(Context &context)
{
std::vector<Volume> volumes;
//FIXME: mock数据
Volume v1 ("1", "a", "cover_1.png", 5);
Volume v2 ("2", "b", "cover_2.png", 1);

volumes.push_back(v1);
volumes.push_back(v2);

boost::shared_ptr<ctemplate::TemplateDictionary> listPageDict(new ctemplate::TemplateDictionary("list"));
for(int i = 0; i < volumes.size(); ++i)
{
ctemplate::TemplateDictionary *listSection = listPageDict->AddSectionDictionary("LIST_SECTIONS");
listSection->SetIntValue("id", volumes[i].id());
listSection->SetValue("name", volumes[i].name());
listSection->SetValue("cover_img_path", volumes[i].cover_path());
}

context.dict = listPageDict;
}
[/cce]
对应的模板文件:
[cce]
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>title</title>
</head>
<body>
<ol>
{{#LIST_SECTIONS}}
<li>name: {{name}}</li>
{{/LIST_SECTIONS}}
</ol>
</body>
</html>
[/cce]
最后输出就是刚在Volume对象的name字段。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据