友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
第三电子书 返回本书目录 加入书签 我的书架 我的书签 TXT全本下载 『收藏到我的浏览器』

Java编程思想第4版[中文版](PDF格式)-第134部分

快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!


  string val;  

public:  

  Pair() { }  

  Pair(char* name; char* value) {  

    nm = decodeURLString(name);  

    val = decodeURLString(value);  

  }  

  const char* name() const { return nm。c_str(); }  

  const char* value() const {   

    return val。c_str();   

  }  

  // Test for 〃emptiness〃  

  bool empty() const {  

    return (nm。length() == 0)   

      || (val。length() == 0);  

  }  

  // Automatic type conversion for boolean test:  

  operator bool() const {  

    return (nm。length() != 0)   

      && (val。length() != 0);  

  }  

  

 (此外,对这个类decodeURLString()会返回一个 string,而不是一个char*)。我们不必定义副本构建 

器、operator=或者破坏器,因为编译器已帮我们做了,而且做得非常好。但即使有些事情是自动进行的, 

C++程序员也必须了解副本构建以及赋值的细节。  

Pair 类剩下的部分由两个方法构成:decodeURLString()以及一个“帮助器”方法translateHex()——将由 

decodeURLString()使用。注意 translateHex()并不能防范用户的恶意输入,比如“%1H”。分配好足够的存 

储空间后(必须由破坏器释放),decodeURLString()就会其中遍历,将所有“+”都换成一个空格;将所有 

十六进制代码(以一个“%”打头)换成对应的字符。  



                                                                                    570 


…………………………………………………………Page 572……………………………………………………………

CGI_vector 用于解析和容纳整个 CGI GET 命令。它是从 STL vector 里继承的,后者例示为容纳Pair 。C++中 

的继承是用一个冒号表示,在Java 中则要用extends。此外,继承默认为private 属性,所以几乎肯定需要 

用到public 关键字,就象这样做的那样。大家也会发现 CGI_vector 有一个副本构建器以及一个 

operator=,但它们都声明成private。这样做是为了防止编译器同步两个函数(如果不自己声明它们,两者 

就会同步)。但这同时也禁止了客户程序员按值或者通过赋值传递一个CGI_vector。  

CGI_vector 的工作是获取QUERY_STRING,并把它解析成“名称/值”对,这需要在Pair 的帮助下完成。它 

首先将字串复制到本地分配的内存,并用常数指针 start 跟踪起始地址(稍后会在破坏器中用于释放内 

存)。随后,它用自己的nextPair()方法将字串解析成原始的“名称/值”对,各个对之间用一个“=”和 

 “&”符号分隔。这些对由 nextPair()传递给 Pair 构建器,所以nextPair()返回的是一个Pair 对象。随后 

用push_back()将该对象加入vector。nextPair()遍历完整个 QUERY_STRING 后,会返回一个零值。  

现在基本工具已定义好,它们可以简单地在一个CGI 程序中使用,就象下面这样:  

  

//: Listmgr2。cpp  

// CGI version of Listmgr。c in C++; which   

// extracts its input via the GET submission   

// from the associated applet。 Also works as  

// an ordinary CGI program with HTML forms。  

#include   

#include 〃CGITools。h〃  

const char* dataFile = 〃list2。txt〃;  

const char* notify = 〃Bruce@EckelObjects。〃;  

#undef DEBUG  

  

// Similar code as before; except that it looks  

// for the email name inside of '':  

int inList(FILE* list; const char* emailName) {  

  const int BSIZE = 255;  

  char lbuf'BSIZE';  

  char emname'BSIZE';  

  // Put the email name in '' so there's no  

  // possibility of a match within another name:  

  sprintf(emname; 〃〃; emailName);  

  // Go to the beginning of the list:  

  fseek(list; 0; SEEK_SET);  

  // Read each line in the list:  

  while(fgets(lbuf; BSIZE; list)) {  

    // Strip off the newline:   

    char * newline = strchr(lbuf; 'n');  

    if(newline != 0)   

      *newline = '0';  

    if(strstr(lbuf; emname) != 0)  

      return 1;  

  }  

  return 0;  

}  

  

void main() {  

  // You MUST print this out; otherwise the   

  // server will not send the response:  

  printf(〃Content…type: text/plainnn〃);  

  FILE* list = fopen(dataFile; 〃a+t〃);  

  if(list == 0) {  



                                                                                             571 


…………………………………………………………Page 573……………………………………………………………

    printf(〃error: could not open database。 〃);  

    printf(〃Notify %s〃; notify);  

    return;  

  }  

  // For a CGI 〃GET;〃 the server puts the data  

  // in the environment variable QUERY_STRING:  

  CGI_vector query(getenv(〃QUERY_STRING〃));  

  #if defined(DEBUG)  

  // Test: dump all names and values  

  for(int i = 0; i 《 query。size(); i++) {  

    printf(〃query'%d'。name() = '%s'; 〃;   

      i; query'i'。name());  

    printf(〃query'%d'。value() = '%s' n〃;   

      i; query'i'。value());  

  }  

  #endif(DEBUG)  

  Pair name = query'0';  

  Pair email = query'1';  

  if(name。empty() || email。empty()) {  

    printf(〃error: null name or email〃);  

    return;  

  }   

  if(inList(list; email。value())) {  

    printf(〃Already in list: %s〃; email。value());  

    return;  

  }  

  // It's not in the list; add it:  

  fseek(list; 0; SEEK_END);  

  fprintf(list; 〃%s ;n〃;   

    name。value(); email。value());  

  fflush(list);  

  fclose(list);  

  printf(〃%s  added to listn〃;   

    name。value(); email。value());  

} ///:~  

  

alreadyInList()函数与前一个版本几乎是完全相同的,只是它假定所有电子函件地址都在一个“”内。  

在使用 GET 方法时(通过在FORM 引导命令的METHOD 标记内部设置,但这在这里由数据发送的方式控制), 

Web 服务器会收集位于“?”后面的所有信息,并把它们置入环境变量QUERY_STRING (查询字串)里。所以为 

了读取那些信息,必须获得QUERY_STRING 的值,这是用标准的C 库函数 getnv()完成的。在 main()中,注意 

对QUERY_STRING 的解析有多么容易:只需把它传递给用于CGI_vector 对象的构建器(名为query),剩下 

的所有工作都会自动进行。从这时开始,我们就可以从query 中取出名称和值,把它们当作数组看待(这是 

由于operator''在vector 里已经过载了)。在调试代码中,大家可看到这一切是如何运作的;调试代码封 

装在预处理器引导命令#if defined(DEBUG)和#endif(DEBUG) 之间。  

现在,我们迫切需要掌握一些与CGI 有关的东西。CGI 程序用两个方式之一传递它们的输入:在 GET 执行期 

间通过QUERY_STRING 传递(目前用的这种方式),或者在POST 期间通过标准输入。但 CGI 程序通过标准输 

出发送自己的输出,这通常是用C 程序的 printf() 命令实现的。那么这个输出到哪里去了呢?它回到了Web 

服务器,由服务器决定该如何处理它。服务器作出决定的依据是content…type (内容类型)头数据。这意味 

着假如 content…type 头不是它看到的第一件东西,就不知道该如何处理收到的数据。因此,我们无论如何也 

要使所有CGI 程序都从 content…type 头开始输出。  

在目前这种情况下,我们希望服务器将所有信息都直接反馈回客户程序(亦即我们的程序片,它们正在等候 

给自己的回复)。信息应该原封不动,所以content…type 设为text/plain (纯文本)。一旦服务器看到这 



                                                                                          572 


…………………………………………………………Page 574……………………………………………………………

个头,就会将所有字串都直接发还给客户。所以每个字串(三个用于出错条件,一个用于成功的加入)都会 

返回程序片。  

我们用相同的代码添加电子函件名称(用户的姓名)。但在CGI 脚本的情况下,并不存在无限循环——程序 

只是简单地响应,然后就中断。每次有一个CGI 请求抵达时,程序都会启动,对那个请求作出反应,然后自 

行关闭。所以CPU 不可能陷入空等待的尴尬境地,只有启动程序和打开文件时才存在性能上的隐患。Web 服 

务器对CGI 请求进行控制时,它的开销会将这种隐患减轻到最低程度。  

这种设计的另一个好处是由于 Pair 和 CGI_vector 都得到了定义,大多数工作都帮我们自动完成了,所以只 

需修改main()即可轻松创建自己的CGI 程序。尽管小服务程序(Servlet)最终会变得越来越流行,但为了 

创建快速的 CGI 程序,C++仍然显得非常方便。  



15。6。4 POST 的概念  



在许多应用程序中使用 GET 都没有问题。但是,GET 要求通过一个环境变量将自己的数据传递给 CGI 程序。 

但假如GET 字串过长,有些Web 服务器可能用光自己的环境空间(若字串长度超过 200 字符,就应开始关心 

这方面的问题)。CGI 为此提供了一个解决方案:POST 。通过POST,数据可以编码,并按与GET 相同的方法 

连结起来。但POST 利用标准输入将编码过后的查询字串传递给CGI 程序。我们要做的全部事情就是判断查询 

字串的长度,而这个长度已在环境变量CONTENT_LENGTH 中保存好了。一旦知道了长度,就可自由分配存储空 

间,并从标准输入中读入指定数量的字符。  

对一个用来控制POST 的CGI 程序,由CGITools。h 提供的Pair 和 CGI_vector 均可不加丝毫改变地使用。下 

面这段程序揭示了写这样的一个CGI 程序有多么简单。这个例子将采用“纯”C++,所以 studio。h 库被 

iostream (IO数据流)代替。对于 iostream ,我们可以使用两个预先定义好的对象:cin,用于同标准输入 

连接;以及 cout,用于同标准输出连接。有几个办法可从cin 中读入数据以及向cout 中写入。但下面这个 

程序准备采用标准方法:用“
返回目录 上一页 下一页 回到顶部 1 1
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!