作者: BUILDER.COM
什么是SOAP
SOAP也被称作XMLP,为两个程序交换信息提供了一种标准的工作机制。在各类机构之间通过电子方式相互协作的情况下完全有必要为此制定相应的标准。
交换信息可以采用很多方法,比如电子邮件、即时聊天和远程过程调用(RPC)等。电子邮件和聊天消息通常不具备计算机友好性。计算机可以读取电子邮件报头,但是其类型内容却无法得到计算机这个“硅脑袋”的理解。即时聊天和RPC也面临同样的尴尬情况:计算机倒是可读可人又没法读了。
计算机确实知道如何理解XML。SOAP描述了把消息捆绑为XML的工作方式。它还说明了发送消息的发送方、消息的内容和地址以及发送消息的时间。这也是为什么把SOAP叫做一种协议的原因。SOAP并没有同电子邮件协议(SMTP)、RPC(套接字和IDL)或者Web协议(HTTP)截然分开。SOAP要利用这些系统作为消息的起点。
程序剖析
程序清单A 和程序清单B(Simple.html和Simple.cgi)所示的简单示例从原理上看都是非常基本的概念。
程序清单A 包含了一个表单,其中有4个字段和一个提交(Submit)按钮。现在在前三个字段中填写内容,比如姓名J. Random、年龄 17及其头发的颜色
brown;然后按下Submit按钮,页面再度显示,第4个字段显示“J. Random is a young brunette”。背景也变为黄色。注意,程序清单A的HTML表单调用了程序清单B中的CGI程序。该程序为浏览页面实施远程服务。如果服务在本地实现,那么你根本用不着提交表单直接采用JavaScript就可以了。
CGI程序相当简单:读取表单数据,返回HTML页面。唯一有点特别的就是参数在被采用之前先收集到了一个%args哈系表里。对这一简单示例而言这样做当然是不必要的,不过,我们的目的就是让所有的表单数据都能放到一个Perl数据结构中来。
现在让我们从协议的角度来审视这个表单提交程序。提交表单时,一个HTTP POST请求会携带表单数据传送给服务器。表单数据由name=value 这样的参数对组成。HTTP响应带回替代的网页。网页可以是任何文档类型。在示例中其文档类型就是“text/html”。
协议内容并不标准。请求内容(表单数据)只包含了表单元素的NAME属性的内容。如果表单元素发生变动,提交的数据也发生相应的改变——两者是链接的。此外,你不可能明晰地发送同一名字对应下的两个数据。所以其灵活性不高。另一方面,返回的文档又太灵活了;它可能只包含一些垃圾。
而且以上这种通信方式也谈不上是一种很清晰的消息系统。发出原始消息的页面发生了改变而非仅仅发送和接收它所需要的内容。Perl可以什么都不返回(一个204“No Content”响应)。但这一响应却并不带回消息给浏览器。从单纯的消息角度来看,这种方法简单太缺乏结构性了,从而带来很多缺点。
用SOAP思想改造表单提交程序
好,我们现在重新考虑Simple.html,新程序是Complex.html(程序清单C),其中有两个框架,分别包含Hidden.html(程序清单D)和Visible.html(程序清单E),Simple.cgi 则更新为Complex.cgi。不过其内容还是很简单。对最终用户的服务没有变。所有改动的无非是来往服务器的消息都模仿SOAP设计和实现。
有改动是必要的但却比较烦琐。Simple.html (程序清单C)现在)现在有了两个表单。表单1(程序清单D)的表单字段和原来的程序一样。表单2(程序清单)上有一个提交按钮和一个隐含字段。在提交表单2的时候,所有的响应都会装入“fhidden”框架。事实上,它就是一个最初的Hidden.html的变种。这样就可以令Visible.html 在不消失的情况下出现了用户眼前。而这恰恰就是SOAP风格的机制了。SOAP在幕后进行消息操作而不取代任何东西。
程序中有两个JavaScript函数, send_request()和get_response()。首先让我们看看send_request()。因为表单2 只有一个字段,表单提交的所有数据都必须包装到这个字段中来。其默认的name=value参数对的方式没法用了,因为现在就只有一个参数对。Send_request()把所有的数据包装到结构里。这种结构根据需要可能很复杂而且还可能嵌套,我们这里当然用的简单了。结构由Perl代码实现的。虽然其名字对应表单1中的字段,不过这只是编程而已。这些数据将在提交表单的时候发送给服务器。
在程序清单F(Complex.cgi)中,发送的Perl数据结构被取了出来。不管其内容到底是什么(只要是Perl数据),eval()代码都能把数据从结构中取出来。这也是SOAP风格的操作——服务器可以处理任何结构数据。数据如何被处理取决于服务器是否认识结构中的字段。在我们这个简单示例里,Complex.cgi 只有在理解内容的情况下才能起作用。用SOAP的术语说就是请求隐含mustUnderstand设置为1(true)。服务器必须理解消息否则就会导致处理失败。在真实的SOAP环境下,你如果理解所应用的约束就必须设置这个标志。
Complex.cgi发回响应,它被装载到“fhidden”框架内。响应消息是一个HTML网页(为了能让咱们的示例工作起来),可是这一响应的本质却是另一种数据结构:一个JavaScript对象。get_response()就在这里派用场了。Visible.html首先装载之后SetInterval()每100毫秒就会调用该函数。当结构中的响应标志位为真的时候get_response()捕获对象并把应答数据放到表单1供你阅读,其任务就到此完成。过程和以上的Perl情形是一样的不过是反了个方向而已。浏览器也必须理解(mustUnderstand)响应,响应是一种结构化的数据,这次是由JavaScript而非Perl来构造的。
所以结构化数据是双向通信的。每一端都必须理解所收到的结构。双方都不必重载(mod_perl中的CGI程序可以)。结构化的数据必须采用收信方能够理解的某种通信语言(服务器一方是Perl,客户机上是JavaScript)。付出的代价则是通信双方程序需要编写结构化数据打包和拆包的有关代码(我们的例子中服务器一方就写了一行代码),如果一方希望自己的请求能得到一个响应,它只能等啊等。
走进SOAP的世界
现在我们就把上面的示例SOAP化。在发送请求时,请求实际上位于一个名叫request的表单字段内。按照SOAP的说法这就叫信封(envelope):把发送的消息装入信封。也就是用SOAP信封代替只有一个字段的表单。信封具体而言就是形如<envelope> 内容 </envelope>的标签对。
假设你的服务器上安装了PHP而非安装的Perl,那么客户端和服务器端的消息就可以对其内容采用同样的语法(JavaScript和PHP碰巧语法很相似)。不过你应该更进一步采用XML作为标准的通信语言。现在的浏览器都已经支持XML数据,服务器也有相应的XML库,所以通信双方都可以读取XML。什么叫SOAP之道?说白了就是利用了部分或者全部的XML标准。
接着我们就在削减JavaScript代码方面打主意了。我们首先升级浏览器使之支持SOAP(比如Mozilla 1.0 / Netscape 7.0)。这就是说打包和拆包的函数唾手可得。你也不必编写这些函数了。这些函数有点类似于采用escape()来获取URL或cookie。这些函数会经常用到。当然,同样的功能也可以通过XSLT来完成。升级的浏览器还可能具有独立于通常的表单提交函数submit()之外的SOAP请求函数。如果是这样你就不必担心被替代的页面了。这一提交函数会等待响应的返回,所以setInterval()就不是必要的了。简而言之,具有SOAP功能的浏览器提供了你需要的接口。
下载本文涉及的文件:perlsoap.zip.
接下来,你必须同通信方必须理解的内容达成一致意见。所以只要发送了消息,就要用额外的数据来包含它,也就是指向描述消息格式的资源的URL。也许这里的资源是DTD或者Xscheme文档。既然接收方总是知道什么是必须理解的内容而不至于瞎猜。那么接收方现在就可以理直气壮地说:我不知道该做什么或者我确实知道该做什么。
最后,如果你愿意,你还可以把浏览器放到一边。仅仅用两个XML服务器程序进行业务对业务的相互交流。通常仍然是采用HTTP请求/响应机制,因为SOAP经常依赖于它们完成大量的工作——甚至在没有涉及到浏览器的情况下也是这样。
小结
SOAP 是一种传递消息的协议。SOAP消息的最常用举措就是请求/响应机制,简单说就是问题和回答。HTML表单提交也是一种请求-响应。SOAP对表单提交有点像Windows对DOS。DOS做了很多基础工作,如果你愿意DOS倒也不赖。Windows复杂多了,但一旦你真正了解和掌握了它就可以获得更强大的功能。你可以用普通的HTML表单模拟SOAP概念,不过,为了真正掌握它,你应该着手学习SOAP语法,同时购买一些相关的开发工具。