65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.50/5 (4投票s)

2007 年 6 月 26 日

4分钟阅读

viewsIcon

60560

downloadIcon

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 样式表
© . All rights reserved.