按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
行:
String emailData =
〃name=〃 + URLEncoder。encode(
name。getText()。trim()) +
〃&email=〃 + URLEncoder。encode(
email。getText()。trim()。toLowerCase()) +
〃&submit=Submit〃;
// Send the name using CGI's GET process:
try {
l。setText(〃Sending。。。〃);
URL u = new URL(
getDocumentBase(); 〃cgi…bin/〃 +
CGIProgram + 〃?〃 + emailData);
l。setText(〃Sent: 〃 + email。getText());
send。setLabel(〃Re…send〃);
l2。setText(
〃Waiting for reply 〃 + ++vcount);
DataInputStream server =
new DataInputStream(u。openStream());
564
…………………………………………………………Page 566……………………………………………………………
String line;
while((line = server。readLine()) != null)
l2。setText(line);
// 。。。
name 和 email 数据都是它们对应的文字框里提取出来,而且两端多余的空格都用trim()剔去了。为了进入列
表,email 名字被强制换成小写形式,以便能够准确地对比(防止基于大小写形式的错误判断)。来自每个
字段的数据都编码为URL 形式,随后采用与 HTML 页中一样的方式汇编GET 字串(这样一来,我们可将Java
程序片与现有的任何CGI 程序结合使用,以满足常规的 HTML GET 请求)。
到这时,一些Java 的魔力已经开始发挥作用了:如果想同任何URL 连接,只需创建一个URL 对象,并将地址
传递给构建器即可。构建器会负责建立同服务器的连接(对 Web 服务器来说,所有连接行动都是根据作为
URL 使用的字串来判断的)。就目前这种情况来说,URL 指向的是当前Web 站点的 cgi…bin 目录(当前Web 站
点的基础地址是用 getDocumentBase()设定的)。一旦Web 服务器在URL 中看到了一个“cgi…bin”,会接着
希望在它后面跟随了cgi…bin 目录内的某个程序的名字,那是我们要运行的目标程序。程序名后面是一个问
号以及CGI 程序会在QUERY_STRING 环境变量中查找的一个参数字串(马上就要学到)。
我们发出任何形式的请求后,一般都会得到一个回应的HTML 页。但若使用Java 的URL 对象,我们可以拦截
自CGI 程序传回的任何东西,只需从 URL 对象里取得一个 InputStream (输入数据流)即可。这是用URL 对
象的openStream()方法实现,它要封装到一个DataInputStream 里。随后就可以读取数据行,若readLine()
返回一个null (空值),就表明CGI 程序已结束了它的输出。
我们即将看到的CGI 程序返回的仅仅是一行,它是用于标志成功与否(以及失败的具体原因)的一个字串。
这一行会被捕获并置放第二个 Label 字段里,使用户看到具体发生了什么事情。
1。 从程序片里显示一个Web 页
程序亦可将 CGI 程序的结果作为一个Web 页显示出来,就象它们在普通 HTML 模式中运行那样。可用下述代码
做到这一点:
getAppletContext()。showDocument(u);
其中,u 代表URL 对象。这是将我们重新定向于另一个Web 页的一个简单例子。那个页凑巧是一个CGI 程序
的输出,但可以非常方便地进入一个原始的HTML 页,所以可以构建这个程序片,令其产生一个由密码保护的
网关,通过它进入自己Web 站点的特殊部分:
//: ShowHTML。java
import java。awt。*;
import java。applet。*;
import java。*;
import java。io。*;
public class ShowHTML extends Applet {
static final String CGIProgram = 〃MyCGIProgram〃;
Button send = new Button(〃Go〃);
Label l = new Label();
public void init() {
add(send);
add(l);
}
public boolean action (Event evt; Object arg) {
if(evt。target。equals(send)) {
try {
// This could be an HTML page instead of
// a CGI program。 Notice that this CGI
// program doesn't use arguments; but
// you can add them in the usual way。
URL u = new URL(
565
…………………………………………………………Page 567……………………………………………………………
getDocumentBase();
〃cgi…bin/〃 + CGIProgram);
// Display the output of the URL using
// the Web browser; as an ordinary page:
getAppletContext()。showDocument(u);
} catch(Exception e) {
l。setText(e。toString());
}
}
else return super。action(evt; arg);
return true;
}
} ///:~
URL 类的最大的特点就是有效地保护了我们的安全。可以同一个Web 服务器建立连接,毋需知道幕后的任何
东西。
15。6。3 用 C++ 写的 CGI 程序
经过前面的学习,大家应该能够根据例子用ANSI C 为自己的服务器写出CGI 程序。之所以选用 ANSI C,是
因为它几乎随处可见,是最流行的 C 语言标准。当然,现在的 C++也非常流行了,特别是采用GNU C++编译器
(g++)形式的那一些(注释④)。可从网上许多地方免费下载g++,而且可选用几乎所有平台的版本(通常
与Linux 那样的操作系统配套提供,且已预先安装好)。正如大家即将看到的那样,从 CGI 程序可获得面向
对象程序设计的许多好处。
④:GNU 的全称是“Gnu's Not Unix”。这最早是由“自由软件基金会”(FSF)负责开发的一个项目,致力
于用一个免费的版本取代原有的Unix 操作系统。现在的 Linux 似乎正在做前人没有做到的事情。但 GNU 工具
在Linux 的开发中扮演了至关重要的角色。事实上,Linux 的整套软件包附带了数量非常多的 GNU 组件。
为避免第一次就提出过多的新概念,这个程序并未打算成为一个“纯”C++程序;有些代码是用普通C 写成
的——尽管还可选用C++的一些替用形式。但这并不是个突出的问题,因为该程序用C++制作最大的好处就是
能够创建类。在解析CGI 信息的时候,由于我们最关心的是字段的“名称/值”对,所以要用一个类
(Pair )来代表单个名称/值对;另一个类(CGI_vector)则将CGI 字串自动解析到它会容纳的Pair 对象里
(作为一个vector),这样即可在有空的时候把每个Pair (对)都取出来。
这个程序同时也非常有趣,因为它演示了 C++与Java 相比的许多优缺点。大家会看到一些相似的东西;比如
class 关键字。访问控制使用的是完全相同的关键字public 和private,但用法却有所不同。它们控制的是
一个块,而非单个方法或字段(也就是说,如果指定private: ,后续的每个定义都具有private 属性,直到
我们再指定 public:为止)。另外在创建一个类的时候,所有定义都自动默认为private。
在这儿使用 C++的一个原因是要利用C++ “标准模板库”(STL)提供的便利。至少,STL 包含了一个 vector
类。这是一个C++模板,可在编译期间进行配置,令其只容纳一种特定类型的对象(这里是 Pair 对象)。和
Java 的Vector 不同,如果我们试图将除Pair 对象之外的任何东西置入 vector,C++的vector 模板都会造成
一个编译期错误;而Java 的Vector 能够照单全收。而且从 vector 里取出什么东西的时候,它会自动成为一
个Pair 对象,毋需进行造型处理。所以检查在编译期进行,这使程序显得更为“健壮”。此外,程序的运行
速度也可以加快,因为没有必要进行运行期间的造型。vector 也会过载operator'',所以可以利用非常方便
的语法来提取Pair 对象。vector 模板将在CGI_vector 创建时使用;在那时,大家就可以体会到如此简短的
一个定义居然蕴藏有那么巨大的能量。
若提到缺点,就一定不要忘记 Pair 在下列代码中定义时的复杂程度。与我们在 Java 代码中看到的相比,
Pair 的方法定义要多得多。这是由于C++的程序员必须提前知道如何用副本构建器控制复制过程,而且要用
过载的 operator=完成赋值。正如第 12 章解释的那样,我们有时也要在Java 中考虑同样的事情。但在C++
中,几乎一刻都不能放松对这些问题的关注。
这个项目首先创建一个可以重复使用的部分,由C++头文件中的 Pair 和 CGI_vector 构成。从技术角度看,
确实不应把这些东西都塞到一个头文件里。但就目前的例子来说,这样做不会造成任何方面的损害,而且更
具有Java 风格,所以大家阅读理解代码时要显得轻松一些:
566
…………………………………………………………Page 568……………………………………………………………
//: CGITools。h
// Automatically extracts and decodes data
// from CGI GETs and POSTs。 Tested with GNU C++
// (available for most server machines)。
#include
#include // STL vector
using namespace std;
// A class to hold a single name…value pair from
// a CGI query。 CGI_vector holds Pair objects and
// returns them from its operator''。
class Pair {
char* nm;
char* val;
public:
Pair() { nm = val = 0; }
Pair(char* name; char* value) {
// Creates new memory:
nm = decodeURLString(name);
val = decodeURLString(value);
}
const char* name() const { return nm; }
const char* value() const { return val; }