C++ CGICC Web 编程

C++ 也是可以用来写 Web 应用程序的,在众所周知的大厂 腾讯,据说还有很多 Web 应用程序是用 C++ 写的

上一章节我们使用 std:cout 配合 Python CGI 容器简单的写了一些 CGI 脚本

如果我们还继续写下去,也不是不可以,但是,要解析参数啦,要处理文件和 Cookie 信息啦,就非常复杂了

还好 GNU 提供了一个开源的 CGI 库 cgicc 可以用来简化开发

你可以点击 ftp://ftp.gnu.org/gnu/cgicc/ 下载最新的版本,截止今天,最新的版本为

下载解压后按照下面的命令来安装这个库

$ tar xzf cgicc-3.2.19.tar.gz
$ cd cgicc-3.2.19  
$ ./configure --prefix=/usr/local
$ make
$ sudo make install

经过漫长的编译等待,终于好了

在练习的过程中,如果有哪些地方不懂,可以查阅 C++ CGI Lib Documentation

其实它有一篇入门文章挺好的 A Tutorial Example

使用 GET 方法传递信息

GET 方法发送已编码的用户信息追加到页面请求中

页面和已编码信息通过 ? 字符分隔开

http://www.twle.cn/cgi-bin/cpp.cgi?name=yufei&base=pek

GET 方法是默认的从浏览器向 Web 服务器传信息的方法,它会在浏览器的地址栏中生成一串很长的字符串

我们日常浏览网页点击链接使用的方法就是 GET

比如我们随便点击 淘宝 中的某个宝贝,就能看到地址栏里长长的字符串

https://detail.tmall.com/item.htm?spm=a230r.1.14.6.231e485aq3hNYE&id=2224961843&cm_id=140105335569ed55e27b&abbucket=17

问号(?) 后面的那些信息就是要发送的信息

因为放在 URL 谁都可以看到,所以要向服务器传密码或其它一些敏感信息时,不要使用 GET 方法

而且 GET 方法有大小限制,在一个请求字符串中最多可以传 1024 个字符

当使用 GET 方法时, CGI 程序中可使用 QUERY_STRING 环境变量来访问

现在我们开发一个 demo 把 URl 上传递的信息都打印出来

我们把淘宝宝贝的链接换成我们自己的,比如下面这样

http://localhost:8080/cgi-bin/item.cgi?spm=a230r.1.14.6.231e485aq3hNYE&id=2224961843&cm_id=140105335569ed55e27b&abbucket=17

那么解析出来就会得到

spm=a230r.1.14.6.231e485aq3hNYE
id=2224961843
cm_id=140105335569ed55e27b
abbucket=17

cgi-bin 目录下创建一个文件 item.cpp 并复制以下内容

/**
 * file: index.cpp
 * author: 简单教程(www.twle.cn)
 *
 * Copyright © 2015-2065 www.twle.cn. All rights reserved.
 */

#include <iostream>

#include <cgicc/CgiDefs.h> 
#include <cgicc/Cgicc.h> 
#include <cgicc/HTTPHTMLHeader.h> 
#include <cgicc/HTMLClasses.h>  

using namespace cgicc;

int main ()
{
    Cgicc formData;

    std::cout << "Content-type:text/html;charset=utf-8\r\n\r\n";
    std::cout << "<!DOCTYPE html>\n";
    std::cout << "<title>使用 GET 传递参数</title>\n";

    form_iterator fi = formData.getElement("spm");  
    if( !fi->isEmpty() && fi != (*formData).end()) {  
        std::cout << "spm:" << **fi << std::endl;  
    }

    std::cout << "<br/>\n";

    fi = formData.getElement("id");  
    if( !fi->isEmpty() && fi != (*formData).end()) {  
        std::cout << "id:" << **fi << std::endl;  
    }

    std::cout << "<br/>\n";

    fi = formData.getElement("cm_id");  
    if( !fi->isEmpty() && fi != (*formData).end()) {  
        std::cout << "cm_id:" << **fi << std::endl;  
    }

    std::cout << "<br/>\n";

    fi = formData.getElement("abbucket");  
    if( !fi->isEmpty() && fi != (*formData).end()) {  
        std::cout << "abbucket:" << **fi << std::endl;  
    }

   return 0;
}

然后使用下面的命令编译我们的 item.cpp

g++ -o item.cgi item.cpp -lcgicc

在浏览器中输入 http://localhost:8080/cgi-bin/item.cgi?spm=a230r.1.14.6.231e485aq3hNYE&id=2224961843&cm_id=140105335569ed55e27b&abbucket=17

可以看到显示如下内容

是不是跟淘宝的一样,哈哈

GET 表单

当然了,要是每次都要输入拷贝这么长长的网址到浏览器肯定不太现实,所以要不我们懒到底,做一个 HTML 网页来提交我们的数据

创建一个 item.html 的文件和 cgi-bin 目录放在一起 (同一级),然后输入以下内容

<!DOCTYPE html>
<meta charset="utf-8" />
<title>GET 表单 - C++ 基础教程 - 简单教程(www.twle.cn)</title>
<p>GET 表单 - C++ 基础教程 - 简单教程(www.twle.cn)</p>
<form action="/cgi-bin/item.cgi" method="get">
<p><label>spm: </label><input size="40" type="text" name="spm" value="a230r.1.14.6.231e485aq3hNYE" /></p>
<p><label>id: </label><input size="40" type="text" name="id" value="2224961843" /></p>
<p><label>cm_id: </label><input size="40" type="text" name="cm_id" value="140105335569ed55e27b"/></p>
<p><label>abbucket: </label><input size="40" type="text" name="abbucket" value="17" /></p>
<p><input type="submit" value="提交" /></p>
</form>

然后点击 http://localhost:8080/item_get.html

就可以打开我们的 GET 表单了

点击提交,是不是到了我们上面做的那个页面


使用 POST 方法传递信息

一个更可靠的向 CGI 程序传递信息的方法是 POST 方法

这种方法打包信息的方式与 GET 方法相同,不同的是,它不是把信息以文本字符串形式放在 URL 中的 ? 之后进行传递,而是把它以单独的消息形式进行传递

POST 方法是以标准输入的形式传给 CGI 脚本的

我们可以复用 item.cgi 程序来处理 POST 方法

然后我们也可以拷贝复用我们的 item_get.htmlitem_post.html 然后把 <form> 中的 method="get" 改成 method="post" 即可

<!DOCTYPE html>
<meta charset="utf-8" />
<title>POST 表单 - C++ 基础教程 - 简单教程(www.twle.cn)</title>
<p>POST 表单 - C++ 基础教程 - 简单教程(www.twle.cn)</p>
<form action="/cgi-bin/item.cgi" method="post">
<p><label>spm: </label><input size="40" type="text" name="spm" value="a230r.1.14.6.231e485aq3hNYE" /></p>
<p><label>id: </label><input size="40" type="text" name="id" value="2224961843" /></p>
<p><label>cm_id: </label><input size="40" type="text" name="cm_id" value="140105335569ed55e27b"/></p>
<p><label>abbucket: </label><input size="40" type="text" name="abbucket" value="17" /></p>
<p><input type="submit" value="提交" /></p>
</form>

然后点击 http://localhost:8080/item_post.html

就可以打开我们的 POST 表单了

点击提交,我们可以发现地址栏中那长长的一串没有了,但数据还是那个数据


向 CGI 程序传递复选框数据

如果需要让用户从一组数据中选择多项,可以使用复选框

我们先创建一个表单 checkbox.html 带有四个复选框

<!DOCTYPE html>
<meta charset="utf-8" />
<title>POST 表单 - C++ 基础教程 - 简单教程(www.twle.cn)</title>
<h3>POST 表单 - C++ 基础教程 - 简单教程(www.twle.cn)</h3>
<form action="/cgi-bin/checkbox.cgi" method="post">
<p>请选择曾经用过的语言</p>
<p>
    <label><input type="checkbox" name="python" value="on" />Python</label>
    <label><input type="checkbox" name="c" value="on" />C</label>
    <label><input type="checkbox" name="java" value="on"/>Java</label>
    <label><input type="checkbox" name="php" value="on" />PHP</label>
</p>
<p><input type="submit" value="提交" /></p>
</form>

然后再开发一个 checkbox.cgi 来接收复选框里的数据

cgi-bin 目录下新建一个文件 checkbox.cpp 然后复制以下内容

/**
 * file: index.cpp
 * author: 简单教程(www.twle.cn)
 *
 * Copyright © 2015-2065 www.twle.cn. All rights reserved.
 */

#include <iostream>

#include <cgicc/CgiDefs.h> 
#include <cgicc/Cgicc.h> 
#include <cgicc/HTTPHTMLHeader.h> 
#include <cgicc/HTMLClasses.h> 

using namespace cgicc;

int main ()
{
    Cgicc formData;
    bool maths_flag, physics_flag;

    std::cout << "Content-type:text/html;charset=utf-8\r\n\r\n";
    std::cout << "<!DOCTYPE html>\n";
    std::cout << "<title>CGI 程序接收复选框数据</title>\n";

    std::cout << "你曾经使用过的语言有: ";

    maths_flag = formData.queryCheckbox("python");
    if( maths_flag ) {  
        std::cout << "Python ";  
    }

    maths_flag = formData.queryCheckbox("c");
    if( maths_flag ) {  
        std::cout << "C ";  
    }

    maths_flag = formData.queryCheckbox("java");
    if( maths_flag ) {  
        std::cout << "Java ";  
    }

    maths_flag = formData.queryCheckbox("php");
    if( maths_flag ) {  
        std::cout << "PHP";  
    }

   return 0;
}

然后使用下面的命令编译我们的 checkbox.cpp

g++ -o checkbox.cgi checkbox.cpp -lcgicc

然后点击 http://localhost:8080/checkbox.html

选择曾经用过的语言,然后点击提交


向 CGI 程序传递单选按钮数据

如果需要用户从一组数据中只能选择一项,可以使用单选框

我们先创建一个表单 radio.html 带有四个单选框

<!DOCTYPE html>
<meta charset="utf-8" />
<title>POST 表单 - C++ 基础教程 - 简单教程(www.twle.cn)</title>
<h3>POST 表单 - C++ 基础教程 - 简单教程(www.twle.cn)</h3>
<form action="/cgi-bin/radio.cgi" method="post">
<p>请选择你喜欢的语言</p>
<p>
    <label><input type="radio" name="language" value="Python" />Python</label>
    <label><input type="radio" name="language" value="C" />C</label>
    <label><input type="radio" name="language" value="Java"/>Java</label>
    <label><input type="radio" name="language" value="PHP" />PHP</label>
</p>
<p><input type="submit" value="提交" /></p>
</form>

然后再开发一个 radio.cgi 来接收单选框的数据

cgi-bin 目录下新建一个文件 radio.cpp 然后复制以下内容

/**
 * file: index.cpp
 * author: 简单教程(www.twle.cn)
 *
 * Copyright © 2015-2065 www.twle.cn. All rights reserved.
 */

#include <iostream>

#include <cgicc/CgiDefs.h> 
#include <cgicc/Cgicc.h> 
#include <cgicc/HTTPHTMLHeader.h> 
#include <cgicc/HTMLClasses.h> 

using namespace cgicc;

int main ()
{
    Cgicc formData;

    std::cout << "Content-type:text/html;charset=utf-8\r\n\r\n";
    std::cout << "<!DOCTYPE html>\n";
    std::cout << "<title>CGI 程序接收单选框数据</title>\n";

    form_iterator fi = formData.getElement("language");  
    if( !fi->isEmpty() && fi != (*formData).end()) { 
        std::cout << "你最喜欢的语言是:" << **fi;  
    } else {
        std::cout << "这么多语言里都没有一个你最喜欢的?"; 
    }

    return 0;
}

然后使用下面的命令编译我们的 radio.cpp

g++ -o radio.cgi radio.cpp -lcgicc

然后点击 http://localhost:8080/radio.html

选择你最喜欢的语言,然后点击提交


向 CGI 程序传递文本框数据

当用户可能输入多行文本时,可以使用文本框 <texarea> 元素

我们先创建一个表单 textarea.html 带有一个文本框

<!DOCTYPE html>
<meta charset="utf-8" />
<title>POST 表单 - C++ 基础教程 - 简单教程(www.twle.cn)</title>
<h3>POST 表单 - C++ 基础教程 - 简单教程(www.twle.cn)</h3>
<form action="/cgi-bin/textarea.cgi" method="post">
<p>请输入你最想说的话</p>
<p><textarea name="words">简单教程,简单编程</textarea></p>
<p><input type="submit" value="提交" /></p>
</form>

然后再开发一个 textarea.cgi 来接收单选框的数据

cgi-bin 目录下新建一个文件 textarea.cpp 然后复制以下内容

/**
 * file: index.cpp
 * author: 简单教程(www.twle.cn)
 *
 * Copyright © 2015-2065 www.twle.cn. All rights reserved.
 */

#include <iostream>

#include <cgicc/CgiDefs.h> 
#include <cgicc/Cgicc.h> 
#include <cgicc/HTTPHTMLHeader.h> 
#include <cgicc/HTMLClasses.h> 

int main ()
{
    cgicc::Cgicc formData;

    std::cout << "Content-type:text/html;charset=utf-8\r\n\r\n";
    std::cout << "<!DOCTYPE html>\n";
    std::cout << "<title>CGI 程序接收文本框数据</title>\n";

    cgicc:: form_iterator fi = formData.getElement("words");  
    if( !fi->isEmpty() && fi != (*formData).end()) {  
        std::cout << "你说了:<br/>" << **fi;  
    }else{
        std::cout << "说点什么好啊吗?";  
    }

   return 0;
}

然后使用下面的命令编译我们的 textarea.cpp

g++ -o textarea.cgi textarea.cpp -lcgicc

然后点击 http://localhost:8080/textarea.html

选择你最喜欢的语言,然后点击提交


向 CGI 程序传递下拉框数据

如果只有三四个选项让用户从中选择一个,用单选框就好了,但如果有八九个,用单选框就太占页面了

这时候可以使用下拉选择框 <select>

我们先创建一个表单 select.html 带有一个下拉选择框

<!DOCTYPE html>
<meta charset="utf-8" />
<title>POST 表单 - C++ 基础教程 - 简单教程(www.twle.cn)</title>
<h3>POST 表单 - C++ 基础教程 - 简单教程(www.twle.cn)</h3>
<form action="/cgi-bin/select.cgi" method="post">
<p>请选择你最喜欢的语言</p>
<p>
    <select name="love">
        <option value="Python">Python</option>
        <option value="PHP">PHP</option>
        <option value="Perl">Perl</option>
        <option value="Ruby">Ruby</option>
        <option value="Scala">Scala</option>
        <option value="C#">C#</option>
        <option value="C++">C++</option>
        <option value="Go">Go</option>
    </select>
</p>
<p><input type="submit" value="提交" /></p>
</form>

然后再开发一个 select.cgi 来接收单选框的数据

cgi-bin 目录下新建一个文件 select.cpp 然后复制以下内容

#include <iostream>

#include <cgicc/CgiDefs.h> 
#include <cgicc/Cgicc.h> 
#include <cgicc/HTTPHTMLHeader.h> 
#include <cgicc/HTMLClasses.h> 

int main ()
{
    cgicc::Cgicc formData;

    std::cout << "Content-type:text/html;charset=utf-8\r\n\r\n";
    std::cout << "<!DOCTYPE html>\n";
    std::cout << "<title>CGI 程序接收下拉框数据</title>\n";

    cgicc::form_iterator fi = formData.getElement("love");  
    if( !fi->isEmpty() && fi != (*formData).end()) {  
        std::cout << "你最喜欢的语言是: " << **fi;  
    } else {
        std::cout << "难道这里面没有你喜欢的语言?";
    }

   return 0;
}

然后使用下面的命令编译我们的 select.cpp

g++ -o select.cgi select.cpp -lcgicc

然后点击 http://localhost:8080/select.html

选择你最喜欢的语言,然后点击提交


在 CGI 中使用 Cookies

HTTP 协议是一种无状态的协议

但现代任何一个网页,都需要在不同页面间保持会话信息

例如,一个用户在完成多个页面的步骤之后结束注册

但是,如何在所有网页中保持用户的会话信息

使用 cookies 是记忆和跟踪有关用户喜好、购买、佣金以及其他为追求更好的游客体验或网站统计所需信息的最有效的方法

Cookie 是如何工作的

服务器以 cookie 的形式向访客的浏览器发送一些数据

如果浏览器接受了 cookie,则 cookie 会以纯文本记录的形式存储在访客的硬盘上

当访客访问网站上的另一个页面时,会检索 cookie。一旦找到 cookie,服务器就知道存储了什么

cookie 是一种纯文本的数据记录,带有 5 个可变长度的字段

字段 说明
Expires cookie 的过期日期。如果此字段未设置,cookie 会在访客退出浏览器时过期
Domain 网站的域名
Path 设置 cookie 的目录或网页的路径。如果让一个域名下的所有网页都可以检索 cookie,此字段可以留空
Secure 如果此字段包含单词 "secure",那么 cookie 只能通过安全服务器进行检索。如果此字段留空,则不存在该限制
Name=Value cookie 以键值对的形式被设置和获取

设置 Cookies

向浏览器发送 cookies 是非常简单的

cookies 会在 Content-type 字段之前,与 HTTP 头一起被发送

下面的代码设置了两个 cookie ,分别是 site=www.twle.cnuser=yufei

cgi-bin 目录下创建一个文件 set_cookie.cpp 并复制以下内容

#include <iostream>

int main ()
{

   std::cout << "Set-Cookie:site=www.twle.cn;\r\n";
   std::cout << "Set-Cookie:user=yufei;";
   std::cout << "Domain=localhost;";
   std::cout << "Path=/;\r\n";
   std::cout << "Content-type:text/html;charset=utf-8\r\n\r\n";

   std::cout << "<!DOCTYPE html>\n";
   std::cout << "<meta charset=\"utf-8\" />";
   std::cout << "<title>CGI 中的 Cookies</title>\n";
   std::cout << "设置 cookies";  

   return 0;
}

使用下面的命令编译我们的 set_cookie.cpp

g++ -o set_cookie.cgi set_cookie.cpp

然后点击 http://localhost:8080/cgi-bin/set_cookie.cgi

可以看到如下内容

打开 Chrome 浏览器的开发者模式,切换到 Application,然后选择 Cookie 可以看到如下内容

获取 Cookies

cookies 存储在 CGI 环境变量 HTTP_COOKIE 中,且它们的形式如下

key1=value1;key2=value2;key3=value3....

cgi-bin 目录下创建一个文件 get_cookie.cpp 并复制以下内容

#include <iostream> 

#include <cgicc/CgiDefs.h> 
#include <cgicc/Cgicc.h> 
#include <cgicc/HTTPHTMLHeader.h> 
#include <cgicc/HTMLClasses.h>

using namespace cgicc;

int main ()
{
    Cgicc cgi;
    const_cookie_iterator cci;

    std::cout << "Content-type:text/html;charset=utf-8\r\n\r\n";
    std::cout << "<!DOCTYPE html>\n";
    std::cout << "<title>CGI 中的 Cookies</title>\n";

    std::cout << "<table border = \"0\" cellspacing = \"2\">";

   // 获取环境变量
   const CgiEnvironment& env = cgi.getEnvironment();

   for( cci = env.getCookieList().begin();
        cci != env.getCookieList().end(); 
        ++cci )
   {
      std::cout << "<tr><td>" << cci->getName() << "</td><td>";
      std::cout << cci->getValue();                                 
      std::cout << "</td></tr>\n";
   }

   std::cout << "</table>\n";
   return 0;
}

使用下面的命令编译我们的 get_cookie.cpp

g++ -o get_cookie.cgi get_cookie.cpp -lcgicc

然后点击 http://localhost:8080/cgi-bin/get_cookie.cgi

可以看到如下内容

文件上传

上传一个文件,需要把 HTML 表单的 enctype 属性设置为 multipart/form-data

我们先创建一个表单 upload.html 带有一个下拉选择框

<!DOCTYPE html>
<meta charset="utf-8">
<title>上传表单 - C++ 基础教程 - 简单教程(www.twle.cn)</title>
<h3>上传表单 - C++ 基础教程 - 简单教程(www.twle.cn)</h3>
<form action="/cgi-bin/upload.cgi" method="post" enctype="multipart/form-data">
    <p>文件:<input type="file" name="userfile" /></p>
    <p><input type="submit" value="上传" /></p>
</form>

然后再开发一个 upload.cgi 来接收上传的文件

cgi-bin 目录下新建一个文件 upload.cpp 然后复制以下内容

#include <iostream>

#include <cgicc/CgiDefs.h> 
#include <cgicc/Cgicc.h> 
#include <cgicc/HTTPHTMLHeader.h> 
#include <cgicc/HTMLClasses.h>

using namespace std;
using namespace cgicc;

int main ()
{
    Cgicc cgi;

    std::cout << "Content-type:text/html;charset=utf-8\r\n\r\n";
    std::cout << "<!DOCTYPE html>\n";
    std::cout << "<title>CGI 中的文件上传</title>\n";

    std::cout << "上传文件的内容是:<br/>";

    // 获取要被上传的文件列表
    const_file_iterator file = cgi.getFile("userfile");
    if(file != cgi.getFiles().end()) {
        // 在 cout 中发送数据类型
        std::cout << HTTPContentHeader(file->getDataType());
        // 在 cout 中写入内容
        file->writeToStream(cout);
   }

   std::cout << "<br/>文件上传成功\n";

   return 0;
}

然后使用下面的命令编译我们的 upload.cpp

g++ -o upload.cgi upload.cpp -lcgicc

然后点击 http://localhost:8080/upload.html

选择上传的文件,然后点击上传


范例是在 cout 流中写入内容,一般情况是打开文件流,并把上传的文件内容保存在目标位置的某个文件中

C++ 基础教程

关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.