查阅Tomcat$CATALINA_BASE/conf/web.xml
文件,很容易发现有个被注释的配置项,即:Common Gateway Includes (CGI) processing servlet,直译为通用网关包含处理servlet。有点拗口,又由于译文中动词“包含”和“处理”都在名词servlet之后,更加显得译文有点让人摸不着头脑;在网上搜索“CGI”,绝大多数返回的结果都是
Common Gateway Interface,在维基百科上找CGI对应的词条解释也是指的后者。
那么,两者有什么区别呢?既然这么相似,两者又有什么联系呢?
先说说通用网关接口,这是网上最常见的,也是广泛认可的说法,也就是说通常“CGI” = “Common Gateway Interface”(通用网关接口)。它能干嘛用呢?它其实是为web服务器定义了一种与外部程序交互的方法,而这类外部程序主要是产生网页内容,通常也被称为CGI程序或CGI脚本程序。简单地说,只要部署在web应用中的脚本程序服从CGI标准或规范,就能被该web服务器的软件调用,从而产生动态内容响应客户端请求。而关于通用网关接口的产生,一方面是由于服务器生成动态页面的需求,另一方面是由于生成动态页面的脚本程序不兼容不复用等问题。详细情况可查阅维基百科上Common Gateway Interface词条或者Tomcat文档User Guide的CGI How to。
那Tomcat中通用网关包含处理servlet又是干嘛用的呢?如果查看了维基百科上的词条会发现,对于通用网关接口,很多著名的web服务器软件都是支持的,如Apache,IIS,Ngnix以及基于node.js服务器等。那么,作为使用较为广泛的Tomcat服务器肯定也是支持的,而Tomcat该如何支持呢?支持的思路也很简单,Tomcat本身不就是一个servlet容器么,它处理servlet的能力不是很好么,那就给它配置一个专门处理通用网关接口(CGI)的servlet就行了。这个配置的专门处理通用网关接口的servlet类就是通用网关包含处理servlet(Common Gateway Includes (CGI) processing servlet)。注意,这个servlet类的主要功能是处理,而处理对象是服务器上的实现了通用网关接口(CGI)的程序或脚本程序,即CGI脚本程序或CGIs。
简单总结下以上内容,通用网关接口(Common Gateway Interface,CGI)是一套专门针对web服务器上web应用中脚本程序的编写标准或规范,只要编写的程序或脚本程序符合该规范,那么web服务器软件就能够根据客户端请求而调用该程序或脚本程序输出结果,并作为动态页面的全部或一部分返回客户端。而Tomcat中,通用网关包含处理servlet(Common Gateway Includes (CGI) processing servlet,CGI processing servlet)是一个在Tomcat服务器上专门处理实现通用网关接口(CGI)的脚本程序的servlet类。
那么,下面以一个Python写的简单CGI脚本程序为例,不依托集成开发环境(IDE),“手动”在Tomcat 配置CGI处理servlet(以下简称CGI处理servlet)及CGI脚本程序。所使用的Tomcat版本为9.0.0.M8。
(1)新建一个web应用项目
在$CATALINA_BASE/webapps/
新建文件夹,文件夹名即为web应用名项目。如图1所示。
打开luckyweb
文件夹,该文件夹即为项目“luckyweb”的web应用根目录,下面以$LUCKYWEB_BASE
代指该目录。。
在该根目录下可以存放web应用的静态内容,如(HTML页面,JSP页面,JavaScript文件,CSS样式表文件和图像文件),客户端都可以直接对此进行访问,比如根目录下新建一个欢迎页面welcome.html
,则可在启动Tomcat后从浏览器地址栏输入http://localhost:8080/luckyweb/welcome.html
进行访问(可自行测试,此处略去)。其中,8080
是Tomcat对一个特定连接器的默认端口号,这个连接器的功能非常重要,它是负责接收所有客户端请求和发送所有服务器响应,可以把它理解为一个“端点(endpoint)”,用于连接客户端和服务器。端口号就是显式告知对该连接器的使用。(详见$CATALINA_BASE/conf/server.xml
)
有了根目录,那么继续新建两个文件夹,分别为META-INF
和WEB-INF
,其对应的文件路径分别为$LUCKYWEB_BASE/META-INF
和$LUCKYWEB_BASE/WEB-INF
,如图2所示。
META-INF
的具体表示含义在Tomcat官方文档或其它资料上暂时没找到较为详细的解释(日后看到,立即补上),但可以肯定的是它应该是代指“metadata-information”,也就是“元数据信息”,文件夹里应包含对本web应用中各类数据的注解和说明。本例由于比较简单,且仅需服务器上本web应用能启用对CGI的使用,所以后面会看到,我们仅仅是在该文件夹中新建context.xml
文件并对其进行配置,也无其它更多深度使用方法和技巧;
WEB-INF
文件夹是该web应用的专属配置文件夹,可以包含web应用部署描述器(web.xml
)、自行创建的定制标签库描述描述器,以及其它希望包含进本web应用的资源文件。servlet官方标准里明确禁止将此文件夹(及其子文件)直接提供给客户端,所以,这里是存储一些敏感配置信息(如数据库连接用户名和密码)的好地方。
(2)新建web.xml,并配置CGI处理servlet类及其servlet映射
在$LUCKYWEB_BASE/WEB-INF
路径下,新建web.xml
文件,官方称呼该文件为web应用部署描述器(Web Application Deployment Descriptor),它是一个描述web应用中servlet和其他组件的XML文件,包括初始化参数和容器管理安全限制等内容。详细内容可参考Tomcat官方文档。注意,若采用记事本新建该文件,其默认编码方式是ANSI,而不是UTF-8,为了避免日后出现字符不识别等问题,建议另存为UTF-8编码格式。
图3给出了在WEB-INF/
文件夹下新建的web.xml
web应用部署描述器:
web.xml
的具体内容如下面代码片所示:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- The definition of CGI processing servlet -->
<servlet>
<servlet-name>cgi</servlet-name>
<servlet-class>org.apache.catalina.servlets.CGIServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>cgiPathPrefix</param-name>
<param-value>WEB-INF/cgi</param-value>
</init-param>
<!-- attach to python -->
<init-param>
<param-name>executable</param-name>
<param-value>python</param-value>
</init-param>
<load-on-startup>5</load-on-startup>
</servlet>
<!-- The mapping for the CGI processing servlet -->
<servlet-mapping>
<servlet-name>cgi</servlet-name>
<url-pattern>/cgi-bin/*</url-pattern>
</servlet-mapping>
</web-app>
其中,<servlet>
至</servlet>
部分是CGI处理servlet类的定义部分,包括servlet名<servlet-name>
、servlet类<servlet-class>
和若干个初始化参数<init-param>
;<servlet-mapping>
至</servlet-mapping>
部分是CGI处理servlet类的servlet映射部分,包括servlet名<servlet-name>
和URL格式<url-pattern>
。
这些内容基本都是Tomcat预先定义或编辑好的(除了参数executable
及其值,后面会讲到),直接从$CATALINA_BASE/conf/web.xml
相应位置复制过来的就行。
下面仅对几个重点问题进行说明:
①关于初始化参数executable
及其值的设置问题。参数executable
的默认值是perl
,这意味着在不指定该参数值的情况下,访问本应用中后缀名为*.cgi
的文件,默认采用perl编译器进行编译执行,而若要采用Python编译器编译执行*.cgi
文件,则须将该值设置为python
。另外,不可忽视的一点是,无论期望采用何种程序语言编译器运行CGI程序,首先要保证搭载服务器的计算机上配置好相应编译器的环境变量或启动路径,否则即使参数值正确,CGI程序也是无法运行。图4给出了已安装Python编译器的计算机命令行调用结果(Win10)。
注:很多时候测试出现问题都是编译器未得到正确启动,所以请务必确认你的编译器正确安装,且启动命令参数值和你配置的executable
参数值一致。
②关于初始化参数cgiPathPrefix
及其值的含义问题。参数cgiPathPrefix
表示的是查找CGI程序的(部分)路径。它的完整路径表达应该是“web应用根目录”+“分隔符”+“参数cgiPathPrefix
值”。简单地说,就是服务器判断出客户端要调用某个CGI程序了,它该去哪里找这个CGI程序。
③关于servlet映射问题。servlet映射其实就是针对特定格式的URL指定专门的servlet负责处理。具体来讲,访问本例中web应用的URL应包含http://localhost:8080/luckyweb*
,“”部分就是需要指定的<url-patten>
’部分,当“”为“/cgi-bin/*”时,服务器就将该请求交由servlet名为cgi
的servlet类进行处理,而这恰恰是上文刚刚配置的servlet类,这样服务器就完成了一个“中间商”的作用,后续对CGI程序的调用和动态页面内容的生成都交由CGI处理servlet类和CGI程序完成。
注意,实际中,web.xml
需要设置的内容或参数是非常多的,由于本例暂不涉及,故未设置,特此澄清,以免误解。
(3)新建cgi文件夹及CGI脚本程序
在$LUCKYWEB_BASE/WEB-INF
路径下新建“cgi”文件夹,如图5所示。
用Python语言编写一简单脚本程序(输出不是很符合HTML文档的要素要求,这里主要是测试下最终调用效果),代码如下:
print ('Content-Type:text/html')
print ()
print ('<h1>Hello python</h1>')
Python文件名后缀通常为“.py”,而要作为CGI脚本运行,需将后缀名改为“.cgi”。故本例中,将该Python语言编写的脚本程序命名为“hello-python.cgi”,且存放路径为$LUCKYWEB_BASE/WEB-INF/cgi
如图6所示:
(4)在web应用中的context.xml中配置Context的属性
CGI处理servlet类和CGI脚本程序就算基本配置完成了,但还有一个关键步骤需要做,是什么呢?那就是需要给本web应用设置“特权”。出于安全考虑,Tomcat服务器默认是不支持CGI脚本程序的(这也就是$CATALINA_BASE/conf/web.xml
CGI处理servlet部分为注释的原因),所以,当要启用CGI处理servlet时,需要对环境或上下文(Context)设置特权。怎么设置呢?很简单,在$LUCKYWEB_BASE/META-INF/
路径下,新建context.xml
文件,如图7所示。
context.xml
文件内代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<Context privileged="true">
</Context>
其中,privileged
是根元素Context
的属性,将其设置为“true”,即可保证该web应用可启用CGI处理servlet及CGI脚本程序运行。
(5)启动Tomcat服务器,并访问CGI脚本程序,查看结果
大功告成,下面启动Tomcat服务器,输入http://localhost:8080/luckyweb/cgi-bin/hello-python.cgi
看下效果吧。
基于IDE方式与此大致相同,建议有兴趣的也动手试试吧。
完。