‹  返回课程

模板数据+输入数据+逻辑处理

课文
阅读量:567
技术范畴
视频中的“学习一点理论”是重点。
课前导言
视频中的“学习一点理论”是重点。
模板数据+输入数据+逻辑处理
依据特写的业务逻辑,组织模板数据和输入数据,形成最终展现

一、模板文件变化自动检测

在各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的四个方法:

  1. ctx->Req() 返回HTTP请求的Requester对象,通过它来取得URL的参数
  2. ctx->ModelData() 返回针对本次HTTP请求的模板内容对应的数据。
  3. 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 是动词:渲染
}
课后补充
加法程序虽然非常简陋,比如说,能不能不在URL里输入加数,能不能在页面上加两个输入框?错误处理能不能从边上或底部浮出?事实上这些都是前端的知识了,和后端使用什么Web Server框架基本无关。