使用 XSLT 为 VBScript 和 JavaScript 生成 Web 服务代理






3.50/5 (4投票s)
2007 年 6 月 26 日
4分钟阅读

60560

468
描述了如何使用 XSLT 样式表将 Web 服务的 WSDL 定义转换为 VBScript/JavaScript 代理代码
引言
本文描述了一个用于生成面向 JavaScript/VBScript 的 Web 代理类的简单代码生成器。该代码生成器本质上是一个 XSLT 文档。
背景
在使用大量使用 VBScript 的 Web 应用程序时,我想要一种简单的方法来访问 Web 服务(用 C# 编写)。由于我找不到适用于 VBScript 的 Web 服务生成工具,所以我使用 XSLT 自己编写了一个。生成等效的 JavaScript 并不难,所以我也将生成 JavaScript 包含在内。这对于希望从 JavaScript 调用 Web 服务但不想使用全功能的 AJAX 框架的程序员可能很有用。实际上,我采用的方法几乎是反 AJAX 的——它是同步的,并且使用字符串处理例程来创建/解析 Web 请求/响应。
使用代码
要生成 VBScript Web 代理代码,需要执行 XSLT 转换。这可以通过几行代码实现
//
// Transform wsdl
//
XslCompiledTransform xsltransform = new XslCompiledTransform();
try
{
string xsltFileName = "WSDLtoVBScript.xslt";
string wsdlSourceFileName = "ServiceX.wsdl";
string vbscriptProxyFileName = "ServiceX.vbs";
xsltransform.Load(xsltFileName);
xsltransform.Transform(wsdlSourceFileName, vbscriptProxyFileName);
}
catch (System.Xml.XmlException theException)
{
// Handle exception
}
包含用于访问 Web 服务的 VBScript 客户端代码的 HTML 文件如下所示
<html>
<head>
<script language="vbscript" src="./Scripts/String.vbs"></script>
<script language="vbscript" src="./Scripts/ServiceXProxy.vbs"></script>
<script language=vbscript>
Dim ServiceXProxy
Sub window_onload
Set ServiceXWS = New ServiceX
ServiceXWS.serviceAddress = "htpp://myhost.com/ServiceX.asmx"
End Sub
Sub Test
window.alert(ServiceX.Test1)
End Sub
</script>
</head>
<body>
...
<input type=button value="Test" id="TestBtn" onclick="vbscript:Test()" />
...
</body>
</html>
上面的示例假定 Web 服务 ServiceX
有一个名为 Test1
的方法,该方法返回一个 string
值。此外,还需要包含文件 String.vbs,因为它包含 Web 代理类所需的字符串处理例程。
XSLT 样式表
XSLT 将生成一个与 Web 服务同名的类(由 service
WSDL 元素的 name
属性指定)。在该类中,为每个方法(即每个 operation
WSDL 元素)定义一个函数。在此之前,为 Web 服务定义的每种新类型创建新的类,并且为每种新类型生成一个 SOAPResponseTo_
(类型名称) 函数,该函数解析 soap 响应并返回该类型的 VBScript 实例。因此,WSDLtoVBScript.xslt 文档的结构如下
- 生成复杂类型的类定义(/wsdl:definitions/wsdl:types/s:schema/s
:complexType[not(contains(@name,'ArrayOf'))]
) - 生成 SOAP 响应解析方法 - 用于简单类型和复杂类型(wsdl:definitions/wsdl:types/s:schema/s:complexType)
- 生成 Web 服务类(/wsdl:definitions/wsdl:service)
- 生成函数定义(/wsdl:operation)
下面将对此进行更详细的描述。
设置输出类型
由于输出是 VBScript/JavaScript 代码,因此输出类型为文本
<xsl:output method="text" />
声明全局变量
一些常用元素值被赋予变量名,以便在脚本的后续部分快速访问,例如,serviceName
变量定义如下
<xsl:variable name="serviceName"
select="/wsdl:definitions/wsdl:service/@name"/>
选择根节点
选择根节点开始转换
<xsl:template match="/">
类定义
为每个复杂类型(非数组)创建一个 VBScript 类,使用 xsl:for-each
元素循环遍历所有适用的复杂类型
<xsl:for-each select="/wsdl:definitions/wsdl:types/s:schema/s:
complexType[not(contains(@name,'ArrayOf'))]">
...
</xsl:for-each>
类定义还包括一个 toString
函数,该函数使用常见的 XSLT“模式”生成逗号分隔的值列表
str = str + "<xsl:if test="position()!=1">, </xsl:if>...
SOAP 响应解析方法
解析简单的 SOAP 响应类型不需要实际的转换,因此这些方法已包含为纯文本(可以将其取出并移至包含脚本)。例如,对于整数类型
Function SOAPResponseTo_int(soapRespText)
SOAPResponseTo_int = CInt(soapRespText)
End Function
对于复杂类型,使用与复杂类型初始类定义相同的 for-each
循环。使用 xsl:choose
元素进行检查,以确定复杂类型是否为数组
<xsl:for-each select="wsdl:definitions/wsdl:types/s:schema/s:complexType">
<xsl:variable name="baseClass"
select="substring-after(s:complexContent/s:extension/@base,':')"/>
Function SOAPResponseTo_<xsl:value-of select="@name"/>(soapRespText)
<xsl:choose>
<xsl:when test="contains(@name,'ArrayOf')">
...
</xsl:when>
<xsl:otherwise>
...
</xsl:otherwise>
</xsl:choose>
End Function
</xsl:for-each>
生成类
Web 服务代理类按如下方式生成
Class <xsl:value-of select="$serviceName"/>
Public serviceAddress
Public serviceNamespace
Private Sub Class_Initialize
me.serviceAddress
= "<xsl:value-of select=_
"/wsdl:definitions/wsdl:service/wsdl:port/soap:address/@location" />"
me.serviceNamespace
= "<xsl:value-of select="$serviceNamespace"/>"
End Sub
<xsl:apply-templates select=_
"wsdl:definitions/wsdl:binding[soap:binding]/wsdl:operation" />
End Class
这是根 xsl:template
节点中的最后一段文本。xsl:apply-templates
元素将导致 wsdl:operation
元素被添加到 XSLT 处理器要处理的节点堆栈中。
生成函数
最后,通过匹配 wsdl:operation
元素的模板来生成函数
<xsl:template match="wsdl:operation">
<xsl:variable name="methodName" select="@name" />
<xsl:variable name="resultType" select=... />
' Method: <xsl:value-of select="$methodName"/>
(for operation '<xsl:value-of select="@name"/>')
' ...
Public Function <xsl:value-of select="$methodName"/>(
<xsl:call-template
name="CallParameterList">
<xsl:with-param name="methodName" select="$methodName"/>
</xsl:call-template>)
' Create the XML HTTP request object
Set req = CreateObject("Microsoft.XMLHTTP")
' Construct the SOAP request
Dim soapReq
soapReq = ...
' Send the SOAP request (synchronously)
req.open "POST", me.serviceAddress, false
req.setRequestHeader "Content-Type", "text/xml; charset=utf-8"
req.setRequestHeader "SOAPAction", me.serviceNamespace +
"<xsl:value-of select="$methodName"/>"
req.send soapReq
' Construct the result
<xsl:choose>
<!-- Simple type result -->
<xsl:when test="($resultType = 's:int') or
($resultType = 's:string') or ...">
<xsl:value-of select="$methodName"/>
= SOAPResponseTo_<xsl:value-of select=
"substring-after($resultType,':')"/>(...)
</xsl:when>
...
</xsl:choose>
End Function
</xsl:template>
通过调用 XSLT 命名模板(类似于过程 - 见下文)通过 xsl:call-template
元素生成函数签名。构造 SOAP 请求,然后使用 Microsoft.XMLHTTP
对象将其发布到 Web 服务地址。然后通过调用适当的 SOAP_ResponseTo_
... 函数来反序列化 SOAP 响应。
命名模板(XSLT 过程)
上面生成的函数 XSLT 大量使用了对命名模板的调用来更好地分解代码。一个例子是 CallParameterList
命名模板
<xsl:template name="CallParameterList">
<xsl:param name="methodName"/>
<xsl:for-each
select="/wsdl:definitions/wsdl:types/s:schema/s:element
[@name=$methodName]/s:complexType/s:sequence/s:element">
<xsl:if test="position()!=1">, </xsl:if><xsl:value-of select="@name" />
</xsl:for-each>
</xsl:template>
JavaScript
对于 JavaScript,过程基本相同。需要将文件 String.js 替换 String.vbs。此外,JavaScript 的原型功能被用来代替 VBScript 中的 Class 构造。
历史
- 2007 年 6 月 27 日 - 发布
- 2007 年 7 月 4 日 - 更详细地描述了 XSLT 样式表