
模板数据+输入数据+逻辑处理
视频中的“学习一点理论”是重点。![]()
课前导言
模板数据+输入数据+逻辑处理
一、模板文件变化自动检测
在各application完成模板加载,svc运行之前,加入对“EnableDetectTemplates(s)”的调用。调用者是svc,入参是间隔秒数。
开发过程中,哪怕程序不改,网页也会频繁修改(布局、字体、颜色等)、因此可以将间隔数设置小一些。本例为了方便授课录屏,直接设置成3秒。实际上线系统建议设置15到30分钟。
#include "daqi/da4qi4.hpp"
using namespace da4qi4;
int main()
{
auto svc = Server::Supply("127.0.0.1", 4098);
auto app = svc->DefaultApp();
app->SetTemplateRoot("/home/zhuangyan/Projects/CPP/daqi_demo/view"); //模板文件根目录
app->InitTemplates();
svc->EnableDetectTemplates(3); //3秒
svc->Run();
}
二、在模板中加入可变内容
2.1 直接展现HTTP报头数据项
index.daqi.HTML 模板内容
<!DOCTYPE html>
<html lang="zh">
<head>
<title>首页</title>
<meta content="text/html; charset=UTF-8">
</head>
<body>
<h1>欢迎来到我的主页</h1>
<a href="/hello">进入问候页面</a>
<p>您正在使用的浏览器: {=_HEADER_("User-Agent")=}</p>
</body>
</html>
_HEADER_() 是大器框架内置的一个可用于模板文件中的函数;“User-Agent”是浏览器访问任意网站页面时,都会自动附加上的身份标识。把函数调用放在网页模板中的“{= =}”中间,运行时就会被框架替换为函数的执行结果。 不需要得重启C++程序,刷新页面,就能看以当前使用的浏览器的身份标志;不同浏览器会有展现不同的内容。
再加上“Host”报头数据项,它用来展现当前浏览器地址栏中使用的网址:
<!DOCTYPE html>
<html lang="zh">
<head>
<title>首页</title>
<meta content="text/html; charset=UTF-8">
</head>
<body>
<h1>欢迎来到我的主页</h1>
<a href="/hello">进入问候页面</a>
<p>您正在使用的浏览器: {=_HEADER_("User-Agent")=}</p>
<p>您正在通过该网址访问本站:{=_HEADER_("Host")=}</p>
</body>
</html>
本机可分别使用“127.0.0.1:4098”和 “localhost:4098”进行访问测试。
2.2 展现URL中的参数
继续修改 index.daqi.HTML:
<!DOCTYPE html>
<html lang="zh">
<head>
<title>首页</title>
<meta content="text/html; charset=UTF-8">
</head>
<body>
<h1>欢迎来到我的主页</h1>
<a href="/hello">进入问候页面</a>
<h2>欢迎,{=_URL_PARAMETER_("name")=} {=_URL_PARAMETER_("称呼")=} ! </h2>
<p>您正在使用的浏览器: {=_HEADER_("User-Agent")=}</p>
<p>您正在通过该网址访问本站:{=_HEADER_("Host")=}</p>
</body>
</html>
这次,请访问“http://127.0.0.1:4098/?name=张三丰&称呼=真人”测试。
三、在C++代码中处理业务逻辑
前面的操作一直是:改网页,刷新浏览器,见效……身为一个C++程序员,我们似乎有点羞愧。接下来我们做一个简单的加法应用。要求网址输入:
http://127.0.0.1:4098/add?a=1&b=2;
然后,返回页面上展现:
1 + 2 = 3
实现思路: - 第一件事,是增加一个新的页面,因此对应需要增加一个新的模板文件,并命名为“add”。 - 显示结果中的 1 + 2 = ,仍然无需动用到C++代码,继续使用_URL_PARAMETER_() 函数即可。 - 只有 结果 3,需要,也适合在C++代码中实现。因为虽然只是加法运算,但毕竟是数学计算,这是C++的性能强项。
前两点的实现内容是新增的 add.daqi.HTML 文件:
<!DOCTYPE html>
<html lang="zh">
<head>
<title>加法</title>
<meta content="text/html; charset=UTF-8">
</head>
<body>
<p>
<!-- 展示内容类似:1 + 2 = 3 -->
{=_URL_PARAMETER_("a")=} + {=_URL_PARAMETER_("b")=} = {=c=}
</p>
</body>
</html>
重点在 “{=c=}”身上,“{==}”仍然用来标识一个可变内容,但其内不再是一个内置函数,而是一个普通的变量名称:“c”。为此我们在C++代码中要做的事变成两件:一是计算 a 加 b的和,二是将和以“c”为名字,填入模板对应位置。
首先为app添加针对访问“/add”的处理,代码大概是:
app->AddHandler(_GET_, "/add", add);
之前我们在svc身上调用AddHandler(),svc最终仍然是调用DefaultApp()以获得app,然后在app身上转发调用AddHandler()。现在我们手上已经有app了,所以就直接在它身上调用。
重点是 add 这个处理函数:
void add(Context ctx)
{
//取得URL参数 a 和 b:
std::string a = ctx->Req().GetUrlParameter("a");
std::string b = ctx->Req().GetUrlParameter("b");
//把字符串转换为整数:
int na = std::stoi(a); //stoi 是 C++11新标中的字符串转换整数的函数
int nb = std::stoi(b);
//本例的核心业务逻辑,其实就这一行:
int c = na + nb;
ctx->ModelData()["c"] = c;
ctx->Render().Pass(); //Render 是动词:渲染
}
重点是ctx的四个方法:
- ctx->Req() 返回HTTP请求的Requester对象,通过它来取得URL的参数
- ctx->ModelData() 返回针对本次HTTP请求的模板内容对应的数据。
- ctx->Render(); Render看起来像个名词,其实是动词,所做的事就是将ModelData()“填入”模板
之前第一节,我们向浏览器返回数据,是这样调用: ctx->Res().ReplayOK(要返回的字符串); 这是没有模板时,比较“低级”或比较“底层”的处理。即自己手工组织返回内容。现在返回内容已经明确由“模板数据”和“业务数据(本例是输入数据)”组合而成——这个过程就叫“渲染”。
所谓的渲染 | Render ,在软件行业里被用来指组织最终展现的这一过程。在后台,将模板数据和实际数据结合以生成“HTML”数据,叫渲染。但紧接着浏览器收到“HTML”数据之后,将它实际展现(画)出来,这也被称为“渲染 | Render”。
现在完整的C++(main.cpp)代码是:
#include "daqi/da4qi4.hpp"
using namespace da4qi4;
void add(Context ctx)
{
//取得URL参数 a 和 b:
std::string a = ctx->Req().GetUrlParameter("a");
std::string b = ctx->Req().GetUrlParameter("b");
//把字符串转换为整数:
int na = std::stoi(a); //stoi 是 C++11新标中的字符串转换整数的函数
int nb = std::stoi(b);
//本例的核心业务逻辑,其实就这一行:
int c = na + nb;
ctx->ModelData()["c"] = c;
ctx->Render().Pass(); //Render 是动词:渲染
}
int main()
{
auto svc = Server::Supply("127.0.0.1", 4098);
auto app = svc->DefaultApp();
app->SetTemplateRoot("/home/zhuangyan/Projects/CPP/daqi_demo/view"); //模板文件根目录
app->InitTemplates();
app->AddHandler(_GET_, "/add", add);
svc->EnableDetectTemplates(3); //3秒
svc->Run();
}
打开浏览器,在地址栏输入 “http://127.0.0.1:4098/add/a=1&b=2”,请得到以下页面内容:
1 + 2 = 3
似乎大功告成,但这个行业里,写业务的程序员心里都有一个人尽皆知的秘密:用于错误处理的代码,经常比正常业务逻辑的处理代码,还要长……毕竟,正确答案只有一个,错误方式却容易花样百出。就以本例而言,用户有一百种花样来搞破坏。
比如:“http://127.0.0.1:4098/add/a=1” , 或者 “http://127.0.0.1:4098/add/a=1&b=T”。都会让后台程序在转换a和b为整数时(也就是那两处 std::stoi() 调用)抛出异常。大器框架将帮我们接住所有我们没处理的异常,以确保程序不因异常未捕获而退出;然后框架多数情况下,将直接断开与前端的网络连接,于是用户将看到一个“连接被重置”的页面。如果是火狐浏览器,这个页面似乎还挺可爱……但这不是我们不主动处理异常的借口。
还好,C++支持异常处理。 加入错误处理后的add函数代码如下:
void add(Context ctx)
{
try
{
//取得URL参数 a 和 b:
std::string a = ctx->Req().GetUrlParameter("a");
std::string b = ctx->Req().GetUrlParameter("b");
//把字符串转换为整数:
int na = std::stoi(a); //stoi 是 C++11新标中的字符串转换整数的函数
int nb = std::stoi(b);
//本例的核心业务逻辑,其实就这一行:
int c = na + nb;
ctx->ModelData()["c"] = c;
}
catch(std::exception const& e)
{
ctx->ModelData()["c"] = std::string("处理入参转换异常.") + e.what();
}
ctx->Render().Pass(); //Render 是动词:渲染
}