- 写在前面的话
- 引言
- 第 1 章 对象入门
- 第 2 章 一切都是对象
- 第 3 章 控制程序流程
- 第 4 章 初始化和清除
- 第 5 章 隐藏实施过程
- 第 6 章 类再生
- 第 7 章 多形性
- 第 8 章 对象的容纳
- 第 9 章 违例差错控制
- 第 10 章 Java IO 系统
- 第 11 章 运行期类型鉴定
- 第 12 章 传递和返回对象
- 第 十三 章 创建窗口和程序片
- 第 14 章 多线程
- 第 15 章 网络编程
- 第 16 章 设计范式
- 第 17 章 项目
- 附录 A 使用非 JAVA 代码
- 附录 B 对比 C++和 Java
- 附录 C Java 编程规则
- 附录 D 性能
- 附录 E 关于垃圾收集的一些话
- 附录 F 推荐读物
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 中写入。但下面这个程序准备采用标准方法:用“<<”将信息发给 cout,并用一个成员函数(此时是 read())从 cin 中读入数据:
//: POSTtest.cpp // CGI_vector works as easily with POST as it // does with GET. Written in "pure" C++. #include <iostream.h> #include "CGITools.h" void main() { cout << "Content-type: text/plain\n" << endl; // For a CGI "POST," the server puts the length // of the content string in the environment // variable CONTENT_LENGTH: char* clen = getenv("CONTENT_LENGTH"); if(clen == 0) { cout << "Zero CONTENT_LENGTH" << endl; return; } int len = atoi(clen); char* query_str = new char[len + 1]; cin.read(query_str, len); query_str[len] = '\0'; CGI_vector query(query_str); // Test: dump all names and values for(int i = 0; i < query.size(); i++) cout << "query[" << i << "].name() = [" << query[i].name() << "], " << "query[" << i << "].value() = [" << query[i].value() << "]" << endl; delete query_str; // Release storage } ///:~
getenv() 函数返回指向一个字串的指针,那个字串指示着内容的长度。若指针为零,表明 CONTENT_LENGTH 环境变量尚未设置,所以肯定某个地方出了问题。否则就必须用 ANSI C 库函数 atoi() 将字串转换成一个整数。这个长度将与 new 一起运用,分配足够的存储空间,以便容纳查询字串(另加它的空中止符)。随后为 cin() 调用 read()。read() 函数需要取得指向目标缓冲区的一个指针以及要读入的字节数。随后用空字符(null)中止 query_str,指出已经抵达字串的末尾,这就叫作“空中止”。
到这个时候,我们得到的查询字串与 GET 查询字串已经没有什么区别,所以把它传递给用于 CGI_vector 的构建器。随后便和前例一样,我们可以自由 vector 内不同的字段。
为测试这个程序,必须把它编译到主机 Web 服务器的 cgi-bin 目录下。然后就可以写一个简单的 HTML 页进行测试,就象下面这样:
<HTML> <HEAD> <META CONTENT="text/html"> <TITLE>A test of standard HTML POST</TITLE> </HEAD> Test, uses standard html POST <Form method="POST" ACTION="/cgi-bin/POSTtest"> <P>Field1: <INPUT TYPE = "text" NAME = "Field1" VALUE = "" size = "40"></p> <P>Field2: <INPUT TYPE = "text" NAME = "Field2" VALUE = "" size = "40"></p> <P>Field3: <INPUT TYPE = "text" NAME = "Field3" VALUE = "" size = "40"></p> <P>Field4: <INPUT TYPE = "text" NAME = "Field4" VALUE = "" size = "40"></p> <P>Field5: <INPUT TYPE = "text" NAME = "Field5" VALUE = "" size = "40"></p> <P>Field6: <INPUT TYPE = "text" NAME = "Field6" VALUE = "" size = "40"></p> <p><input type = "submit" name = "submit" > </p> </Form> </HTML>
填好这个表单并提交出去以后,会得到一个简单的文本页,其中包含了解析出来的结果。从中可知道 CGI 程序是否在正常工作。
当然,用一个程序片来提交数据显得更有趣一些。然而,POST 数据的提交属于一个不同的过程。在用常规方式调用了 CGI 程序以后,必须另行建立与服务器的一个连接,以便将查询字串反馈给它。服务器随后会进行一番处理,再通过标准输入将查询字串反馈回 CGI 程序。
为建立与服务器的一个直接连接,必须取得自己创建的 URL,然后调用 openConnection() 创建一个 URLConnection。但是,由于 URLConnection 一般不允许我们把数据发给它,所以必须很可笑地调用 setDoOutput(true) 函数,同时调用的还包括 setDoInput(true) 以及 setAllowUserInteraction(false)——注释⑥。最后,可调用 getOutputStream() 来创建一个 OutputStream(输出数据流),并把它封装到一个 DataOutputStream 里,以便能按传统方式同它通信。下面列出的便是一个用于完成上述工作的程序片,必须在从它的各个字段里收集了数据之后再执行它:
//: POSTtest.java // An applet that sends its data via a CGI POST import java.awt.*; import java.applet.*; import java.net.*; import java.io.*; public class POSTtest extends Applet { final static int SIZE = 10; Button submit = new Button("Submit"); TextField[] t = new TextField[SIZE]; String query = ""; Label l = new Label(); TextArea ta = new TextArea(15, 60); public void init() { Panel p = new Panel(); p.setLayout(new GridLayout(t.length + 2, 2)); for(int i = 0; i < t.length; i++) { p.add(new Label( "Field " + i + " ", Label.RIGHT)); p.add(t[i] = new TextField(30)); } p.add(l); p.add(submit); add("North", p); add("South", ta); } public boolean action (Event evt, Object arg) { if(evt.target.equals(submit)) { query = ""; ta.setText(""); // Encode the query from the field data: for(int i = 0; i < t.length; i++) query += "Field" + i + "=" + URLEncoder.encode( t[i].getText().trim()) + "&"; query += "submit=Submit"; // Send the name using CGI's POST process: try { URL u = new URL( getDocumentBase(), "cgi-bin/POSTtest"); URLConnection urlc = u.openConnection(); urlc.setDoOutput(true); urlc.setDoInput(true); urlc.setAllowUserInteraction(false); DataOutputStream server = new DataOutputStream( urlc.getOutputStream()); // Send the data server.writeBytes(query); server.close(); // Read and display the response. You // cannot use // getAppletContext().showDocument(u); // to display the results as a Web page! DataInputStream in = new DataInputStream( urlc.getInputStream()); String s; while((s = in.readLine()) != null) { ta.appendText(s + "\n"); } in.close(); } catch (Exception e) { l.setText(e.toString()); } } else return super.action(evt, arg); return true; } } ///:~
⑥:我不得不说自己并没有真正理解这儿都发生了什么事情,这些概念都是从 Elliotte Rusty Harold 编著的《Java Network Programming》里得来的,该书由 O'Reilly 于 1997 年出版。他在书中提到了 Java 连网函数库中出现的许多令人迷惑的 Bug。所以一旦涉足这些领域,事情就不是编写代码,然后让它自己运行那么简单。一定要警惕潜在的陷阱!
信息发送到服务器后,我们调用 getInputStream(),并把返回值封装到一个 DataInputStream 里,以便自己能读取结果。要注意的一件事情是结果以文本行的形式显示在一个 TextArea(文本区域)中。为什么不简单地使用 getAppletContext().showDocument(u) 呢?事实上,这正是那些陷阱中的一个。上述代码可以很好地工作,但假如试图换用 showDocument(),几乎一切都会停止运行。也就是说,showDocument() 确实可以运行,但从 POSTtest 得到的返回结果是“Zero CONTENT_LENGTH”(内容长度为零)。所以不知道为什么原因,showDocument() 阻止了 POST 查询向 CGI 程序的传递。我很难判断这到底是一个在以后版本里会修复的 Bug,还是由于我的理解不够(我看过的书对此讲得都很模糊)。但无论在哪种情况下,只要能坚持在文本区域里观看自 CGI 程序返回的内容,上述程序片运行时就没有问题。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论