虽然好久之前就据说“早期的网站不少经过cgi形式实现”、“C++可经过CGI形式编写网页”,日积月累对CGI也有了一些概念,但一直没真正见过一个实际运行的CGI网站,总归仍是有些底气不足。html
上周在菜鸟教程上看到有CGI的编程实现因此就模仿实现一下,而过程当中发现不能成功运行(实际上是本身的未指定脚本处理shell的问题),而后又百度其余资料看到实现方式五花八门不是人云亦云就是实现很不规范,因此本身记录一下。前端
本文实现环境:Ubuntu 16.04 + Python 3.7 + apache2python
Ubuntu(严谨点应该说Dibian)上apache的配置文件在/etc/apache2的目录,咱们先来看其目录结构,以下:ios
xxx-available文件夹表示当前apache2支持的功能,对应的xxx-enabled表示启用的功能;xxx-enabled下的文件通常是xxx-available下的文件的软连接。c++
其中magic定义的是一些MIME的东西(?),envvars定义一些供其余配置文件使用的变量,这两个通常不用咱们修改来看咱们须要修改的:web
/etc/apache2/ |-- apache2.conf # 该文件经过IncludeOptional包含如下(各目录中的)配置文件 | `-- ports.conf # 该文件主要配置监听端口 |-- mods-enabled # 该目录下的文件是针对某个模块的配置 | |-- *.load # .load文件通常是用LoadModule命令加载模块对应的.so文件 | `-- *.conf # .conf文件通常是同名.load文件加载的模块的自己须要的配置,通常不用咱们管 |-- conf-enabled # 该目录下的文件通常是针对某项功能的配置 | `-- *.conf `-- sites-enabled # 该目录下的文件用于配置VirtualHost `-- *.conf
apache被分红这么多配置文件,是为了让咱们方便地知道某项功能对应的配置功是什么;固然对于不熟悉的人则可能感受到的不是方便而是复杂,此时就要知道这只是一种约定的组织形式而并非语法上的强制,因此你要全部配置全都一把写在apache2.conf这文件中也是能够的(但注意apache2为先配置先生效后配置不生效原则,若是IncludeOptional在前且已配置某项的值那你在apache2.conf追加的该项值会不生效)。shell
从上节的介绍中,咱们能够总结出想启用某项功能就在xxx-enabled目录下,建立指向该功能在xxx-available目录下相应文件的软连接;要停用某项功能就删除该功能在xxx-enable目录下相应的软连接。apache
这操做是可行的,但Ubuntu还直接提供了命令来实现该功能,这能够免除咱们切换目录、忘记软连接命令书写格式、遗漏连接等困挠。编程
mod对应的启停用使令是a2enmod/a2dismod,conf对应的启停用命令是a2enconf/a2disconf,site对应的启停用命令是a2ensite/a2dissite。vim
好比咱们这里想启用停用cgi支持功能对应的命令就是:
# cgi和cgid这两个模块的区别是什么我还不太懂,彷佛其实只用cgid就能够了(?) # 启用,cgi、cgid这两个名字是mods-available目录下的文件的名字 sudo a2enmod cgi cgid # 停用,cgi、cgid这两个名字是mods-enabled目录下要删除掉的软连接的名字 sudo a2dismod cgi cgid
可能有小伙伴仍是感受没有很方便啊,我仍是获得mods-available或mods-enabled下看文件的名字;那其实还有另外的写法,你能够先不带要启用/停用的功能的名字,而后该命令就会列出当前全部可启用/停用的功能的名字,此时你再输入想要启用/停用的功能的名字便可,以下图:
总结第二大节知识,使用如下命令启用:
# 启用模块支持。仍是那句话我暂时没懂cgi和cgid的区别,因此索性两个都启用 sudo a2enmod cgi cgid # 启用cgi的默认配置。 sudo a2enconf serve-cgi-bin # 重启apache使用置生效 sudo systemctl restart apache2
这里的针对性改造指两点,一是咱们前面只是启动了cgi咱们还没给apache指出要处理什么语言的cgi,二是咱们想指定cgi文件的目录为/var/www/cgi-bin。
咱们先到/etc/apache2/conf-enabled目录下查看serve-cgi-bin.conf的配置,默认以下:
<IfModule mod_alias.c> <IfModule mod_cgi.c> Define ENABLE_USR_LIB_CGI_BIN </IfModule> <IfModule mod_cgid.c> Define ENABLE_USR_LIB_CGI_BIN </IfModule> <IfDefine ENABLE_USR_LIB_CGI_BIN> ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/ <Directory "/usr/lib/cgi-bin"> AllowOverride None Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch Require all granted </Directory> </IfDefine> </IfModule> # vim: syntax=apache ts=4 sw=4 sts=4 sr noet
能够看到一是没有配置处理文件,二是设置的文件夹是/usr/lib/cgi-bin/。咱们使用"AddHandler cgi-script .cgi .py"指定cgi指定扩展名为.cgi和.py的文件(通常无论什么语言写的都应保存成.cgi但对于pytho也容许保存成习惯的.py),另使用“ScriptAlias /cgi-bin/ /var/www/cgi-bin/”指定遇到路径为“/cgi-bin/”的url就转向“/var/www/cgi-bin/”目录下找文件,最终修改以下:
<IfModule mod_alias.c> <IfModule mod_cgi.c> Define ENABLE_USR_LIB_CGI_BIN </IfModule> <IfModule mod_cgid.c> Define ENABLE_USR_LIB_CGI_BIN </IfModule> <IfDefine ENABLE_USR_LIB_CGI_BIN> ScriptAlias /cgi-bin/ /var/www/cgi-bin/ <Directory "/var/www/cgi-bin/"> AllowOverride None Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch Require all granted AddHandler cgi-script .cgi .py </Directory> </IfDefine> </IfModule> # vim: syntax=apache ts=4 sw=4 sts=4 sr noet
注意两点:
一是开头的#!指定文件解析shell不可少且shell要改为本身python所在路径(不然报错“End of script output before headers:”),由于咱们虽然指定了cgi处理.py文件,但本质上apache仍是不懂.py应该使用哪一个shell去处理.py文件。
二是大多数教程都是上来直接写print(),我这里特意写成了一个方法的形式,主要是为了强调cgi最终就是python helloworld.py这么运行的,你最终输出一个html响应就行,并不须要你上来就print()。
# 建立/var/www/cgi-bin/目录 sudo mkdir /var/www/cgi-bin/ # 建立helloworld.py文件并写入内容 # 我不清楚为何sudo直接cat写入会提示Permission denied因此搞这么麻烦 sudo touch /var/www/cgi-bin/helloworld.py sudo chmod 777 /var/www/cgi-bin/helloworld.py sudo cat > /var/www/cgi-bin/helloworld.py << EOF #!/opt/miniconda3/bin/python def main(): print ("Content-type:text/html") print () # 空行,告诉服务器结束头部 print ('<html>') print ('<head>') print ('<meta charset="utf-8">') print ('<title>Hello Word - My First CGI Programe !</title>') print ('</head>') print ('<body>') print ('<h2>Hello Word! This is my first CGI programe !</h2>') print ('</body>') print ('</html>') main() EOF # 若是自使使用vi写入的文件那么写完后记得给文件添加可执行权限
因为在前边作了不少配置,为了以防万一建议在访问前重启一把apache:
sudo systemctl restart apache2
运行效果以下:
请求-响应数据包过程以下:
咱们在/var/www/cgi-bin目录下新建一个error.py文件,写入如下内容并保存(主要是没有指定shell那一行运行会报错):
def main(): print("Content-type:text/html") print() # 空行,告诉服务器结束头部 print('<html>') print('<head>') print('<meta charset="utf-8">') print('<title>Hello Word - My First CGI Programe !</title>') print('</head>') print('<body>') print('<h2>Hello Word! This is my first CGI programe !</h2>') print('</body>') print('</html>') main()
重启apache后访问两次error.py页面两次,而后查看错误日志(默认是/var/log/apache2/error.log)末尾的内容,若是没有意外应该是以下的两个报错;报错没有什么问题,问题是咱们能够看到两次访问报错的进程id是不同的,这就认证了cgi每次都从新启动一个进程的说法(本质差很少和本身手动运行python文件差很少)。
应该来讲到上一大节咱们的内容已经都讲完了,但一方面python是一种解析型语言到C++等编译型语言是否是同样总仍是不敢肯定,另外一方面按网上的说法C++对apache的配置彻底和python同样,在已有环境仍是比较容易实现因此咱们来看一下。
第一步,在/var/www/cgi-bin目录建立cplusplus.cpp文件、添加可执行权限、写入如下内容:
#include <iostream> using namespace std; int main () { cout << "Content-type:text/html\r\n\r\n"; cout << "<html>\n"; cout << "<head>\n"; cout << "<title>Hello World - First CGI Program</title>\n"; cout << "</head>\n"; cout << "<body>\n"; cout << "<h2>Hello World! This is my first CGI program</h2>\n"; cout << "</body>\n"; cout << "</html>\n"; return 0; }
第二步,使用g++进行编译(不能使用gcc否则会因gcc不会自动连接c++库而报错“cplusplus.cpp:(.text+0xa): undefined reference to `std::cout'”)并把输出文件后辍名指定为.cgi。
sudo g++ -o cplusplus.cgi cplusplus.cpp
因为编译成了可执行文件因此cpp文件不用指定解析器语句也就很好理解了,直接执行输出以下(因此咱们是否能够认为web的本质就是中间件把程序输出返回到前端?):
第三步,访问页面确认效果。以下图:
参考:
https://www.runoob.com/python3/python3-cgi-programming.html
https://www.tutorialspoint.com/cplusplus/cpp_web_programming