级别: 中级 Nicholas Chase (nicholas@nicholaschase.com)Studio B
2004 年 9 月 01 日 典型的 HTML 表单一次只允许提交一个 URL,这样就难以从多个 Web 服务中检索信息。这篇技巧说明如何使用 XForms 通过从单个表单中进行多次提交来解决这个问题。
传统的 HTML 表单通常被用来从用户那里获取信息或者向用户提供信息,与此不同的是,Web 服务最终设计成为应用程序提供信息,用户放在次要的地位。这意味着,虽然从一个地方提交一个 HTML 表单并获得响应一般就足够了,但是常常还需要向 Web 服务提交一个请求,然后使用响应作为第二个请求的基础。
在这篇技巧中,您将创建一个 XForms 表单,提供关于一系列温度传感器的信息。表单接受一个传感器标识符,并将它提交给 Web 服务,该服务返回传感器所在的邮政区号。然后,表单自动将邮政区号提交给第二个服务,再由该服务提供邮政区号对应地点的环境温度。本技巧假设您对 XForms 具有一般性的了解,并且安装了 FormsPlayer XForms 客户程序。(有关的更多信息,请参阅
参考资料。)
基本的请求表单
首先创建一个基本表单(如清单 1 所示),用户可以在该表单中输入传感器 ID:
清单 1. 基本的 XForms 表单
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:xforms="http://www.w3.org/2002/xforms"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:ev="http://www.w3.org/2001/xml-events"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
>
<head>
<title>XForms and Web Services</title>
</head>
<body>
<object id="FormsPlayer"
classid="CLSID:4D0ABA11-C5F0-4478-991A-375C4B648F58">
<b>FormsPlayer has not loaded. Please check your installation.</b>
</object>
<?import namespace="xforms" implementation="#FormsPlayer" ?>
<xforms:model id="WeatherService">
<xforms:instance id="sensorInstance">
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<ns1:getZip xmlns:ns1="urn:example-sensorZip"
SOAP-ENV:encodingStyle=
"http://schemas.xmlsoap.org/soap/encoding/">
<sensorId xsi:type="xsd:string"></sensorId>
</ns1:getZip>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
</xforms:instance>
</xforms:model>
<xforms:input ref="instance('sensorInstance')//sensorId">
<xforms:label>Sensor ID: </xforms:label>
<xforms:hint>Enter the ID for the sensor whose ambient
temperature you want to check.</xforms:hint>
</xforms:input>
</body>
</html>
|
这是一个非常简单的表单,它允许用户输入一个值,如图 1 所示。该值代表
sensorInstance 实例中
sensorId 元素的内容。
sensorInstance 完全采用 SOAP 请求的格式,可以将它发送到 Web 服务并获得响应。
图 1: 基本表单
提交到第一个服务
表单的第一部分获得传感器 ID,并将它提交到返回邮政区号的 Web 服务。为此,必须添加一个
submission 元素和发送它的
trigger ,如清单 2 所示。
清单 2. 提交表单
...
<xforms:model id="WeatherService">
<xforms:instance id="sensorInstance">
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<ns1:getZip xmlns:ns1="urn:example-sensorZip"
SOAP-ENV:encodingStyle=
"http://schemas.xmlsoap.org/soap/encoding/">
<sensorId xsi:type="xsd:string"></sensorId>
</ns1:getZip>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
</xforms:instance>
<xforms:submission id="getzip"
method="text-xml-post"
replace="instance"
ref="instance('sensorInstance')"
action="http://www.nicholaschase.com/sensors/getZipService.php"
/>
</xforms:model>
<xforms:switch id="switch1">
<xforms:case id="requestGUI">
<xforms:input ref="instance('sensorInstance')//sensorId">
<xforms:label>Sensor ID: </xforms:label>
<xforms:hint>Enter the ID for the sensor whose ambient
temperature you want to check.</xforms:hint>
</xforms:input>
<xforms:trigger style="display:block">
<xforms:label>Get sensor ambient temperature</xforms:label>
<xforms:action ev:event="DOMActivate">
<xforms:send submission="getzip" />
<xforms:toggle case="responseGUI" />
</xforms:action>
</xforms:trigger>
</xforms:case>
<xforms:case id="responseGUI">
<xforms:output ref="instance('sensorInstance')//return">
<xforms:label>Sensor zip code:</xforms:label>
</xforms:output>
</xforms:case>
</xforms:switch>
</body>
</html>
|
单击
trigger 按钮时发生了两件事。首先,浏览器发送
getzip 提交。该提交在模型中使用
submission 元素定义,该元素确定了要提交的数据和提交的目的地。在本示例中,向服务发送的是
sensorInstance 数据,服务在响应中返回一个 SOAP 消息。真正的邮政区号在
return 元素中。
发送提交之后,表单从显示原始表单的
requestGUI 切换到
responseGUI ,该界面显示了返回的数据(如图 2 所示)。
图 2: 第一次响应
第二次提交
获得邮政区号之后,必须找到一种方法将其发送给气象服务。首先要创建一个实例,并将它提交给服务,如清单 3 所示:
清单 3. 添加第二个实例
...
<xforms:model id="WeatherService">
<xforms:instance id="sensorInstance">
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<ns1:getZip xmlns:ns1="urn:example-sensorZip"
SOAP-ENV:encodingStyle=
"http://schemas.xmlsoap.org/soap/encoding/">
<sensorId xsi:type="xsd:string"></sensorId>
</ns1:getZip>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
</xforms:instance>
<xforms:instance id="weatherInstance">
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<ns1:getTemp xmlns:ns1="urn:xmethods-Temperature"
SOAP-ENV:encodingStyle=
"http://schemas.xmlsoap.org/soap/encoding/">
<zipcode xsi:type="xsd:string"></zipcode>
</ns1:getTemp>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
</xforms:instance>
<xforms:submission id="getzip"
method="text-xml-post"
replace="instance"
ref="instance('sensorInstance')"
action="http://www.nicholaschase.com/sensors/getZipService.php"
/>
<xforms:submission id="getweather"
method="text-xml-post"
replace="instance"
ref="instance('weatherInstance')"
action="http://services.xmethods.net:80/soap/servlet/rpcrouter"
/>
</xforms:model>
...
|
总的思路和前面是一样的:创建一个类似 Web 服务请求的实例,然后按照
submission 元素中的定义,将它发送到 Web 服务。现在要做的就是找到一种方法,取得第一次提交所返回的邮政区号,并在提交之前将它放到第二个实例中。所幸的是,可以使用
setvalue 元素完成这一工作:
清单 4. 连接两次提交
...
<xforms:submission id="getweather"
method="text-xml-post"
replace="instance"
ref="instance('weatherInstance')"
action="http://services.xmethods.net:80/soap/servlet/rpcrouter"
/>
</xforms:model>
<xforms:switch id="switch1">
<xforms:case id="requestGUI">
<xforms:input ref="instance('sensorInstance')//sensorId">
<xforms:label>Sensor ID: </xforms:label>
<xforms:hint>Enter the ID for the sensor whose ambient
temperature you want to check.</xforms:hint>
</xforms:input>
<xforms:trigger style="display:block">
<xforms:label>Get sensor ambient temperature</xforms:label>
<xforms:action ev:event="DOMActivate">
<xforms:send submission="getzip" />
<xforms:setvalue value="instance('sensorInstance')//return"
ref="instance('weatherInstance')//zipcode"/>
<xforms:send submission="getweather" />
<xforms:toggle case="responseGUI" />
</xforms:action>
</xforms:trigger>
</xforms:case>
<xforms:case id="responseGUI">
<xforms:output ref="instance('sensorInstance')//return">
<xforms:label>Sensor zip code:</xforms:label>
</xforms:output>
<br />
<xforms:output ref="instance('weatherInstance')//return">
<xforms:label>Ambient sensor temperature:</xforms:label>
</xforms:output>
</xforms:case>
</xforms:switch>
</body>
</html>
|
观察
trigger 就会发现,实际上只是添加了浏览器要执行的动作。提交第一个请求之后,
sensorInstance 实例中的
return 元素将包含需要的邮政区号。使用
setvalue 元素可以将这个值复制到
weatherInstance 实例中,然后提交该实例。结果显示在如图 3 所示的页面中。
图 3. 最终结果
结束语
XForms 的设计把显示数据的过程与处理数据的过程分离了开来,因此非常适合在单次请求中执行多重提交。这个过程需要为每个服务创建一个实例,而且每个实例都包含特定服务的 SOAP 请求。当用户触发事件时(比如激活一个按钮),可以执行任意多个动作,因为每个实例都可以有自己的提交,所以您要做的只是依次提交每个实例,并使用
setvalue 元素在实例间移动数据。
参考资料
关于作者  | |  | Nicholas Chase 是 Studio B 的作者,曾參與過許多公司網站的開發,其中包括 Lucent
Technologies、Sun Microsystems、Oracle 和 Tampa Bay Buccaneers。Nick 曾經做過高中物理教師、低級放射性廢棄設備管理員、線上科幻雜誌的編輯、多媒體工程師以及 Oracle 教員。最近,他在擔任美國佛羅里達州克利爾沃特市一家交互通信公司的首席技術官,同時,他還是幾本 Web 開發書籍的作者,其中包括 XML
Primer Plus(Sams)。目前,他正準備購買一座農場,打算和妻子飼養羊駝和小雞。他很想聽到讀者的意見,您可以透過nicholas@nicholaschase.com 與他聯繫。 |
对本文的评价
|