WebSphere Web 服务交互的 .Net 客户端

来源:互联网 发布:通达信boll指标源码 编辑:程序博客网 时间:2024/06/05 09:20
互操作性是在开发 Web 服务架构所做的最大的一个保证之一。这是一种使不同应用程序可以一起工作的能力,即使这些应用程序运行在不同的操作系统、不同的硬件体系上,并且使用了不同的应用程序基础架构。本文讨论了互操作性的一个重要的、实用的案例--编写一个运行在 Windows 系统上及用 Visual Basic 语言编写的 .Net 客户端应用程序,在程序中通过 Web 服务链接到一个服务器端应用程序,该服务器端应用程序用 Java 编程语言编写,运行于 WebSphere Application Server。

文使用 WebSphere SDK for Web Services V5.1 (WSDK 5.1) 作为案例的服务器端。为了实现 Windows 客户端应用程序,本文使用了 Visual Basic .Net 。当然,您也可以同样使用其他客户端编程语言,例如 Visual C。本文讲述的这个简单案例中,我们使用 WSDK 5.1 附带的 Sample1 Web 服务。

在本文中的 .Net 客户端是使用 Visual Basic .Net Development Environment 2003 (Version 7.1.3088) 和 Microsoft .Net Framework 1.1 (Version 1.1.4322) 开发的。

在本文中不提供对 Visual Basic .Net 本身的指导,而是集中于如何使用 Visual Basic .Net 开发 Web 服务客户端,它可以调用基于 Java 的使用 WSDK 开发的 Web 服务。

注意:Web 服务的范例都是使用  Wrapped Doc/Literal 形式的 WSDL。这是很重要的一点,因为其他形式的 WSDL (例如, RPC 形式)不能接受使用 Microsoft 工具产生的 WSDL 版本。

本文的先决条件

如果您想按照本文自己构建应用程序,您需要 Windows 系统,并在上面安装 Microsoft Visual Basic .Net(或者 Microsoft Visual Studio .Net)。您还需要一个操作系统(Windows 或者 Linux)来安装 WebSphere SDK for Web Services V5.1 。

如果这个系统的 RAM 和硬盘存储足够大,您可以安装 Windows 版本的 WSDK 5.1 和 Microsoft 工具到同一个 Windows 系统上。另外一个可选择的方案是,WSDK 5.1 和 Microsoft 工具安装在不同的机器上,并通过 TCP/IP 来实现网络连接。





回页首

创建 .Net 客户端 GUI

在 Microsoft Visual Basic .Net 中创建一个新的 Windows Application 项目——我们将它命名为 Sample1WebService。

创建一个新的窗体,并在上面添加三个文本框,以及一个标签和两个按钮,界面看起来如 图 1



样本 Visual Basic 应用程序窗体

对于窗体,必须注意以下两点:

  • Web host 文本框有一个默认值 “localhost”。这是假定 WSDK 和 Visual Basic 安装于同一台机器。将这个文本框作为一个变量,以便允许用户方便地运行客户端应用程序,哪怕是管理 Web 服务的主机和客户端安装在不同的机器上,因为这样做您只需提供机器的网络名称就可以了。在后面,我们将看到这个值会使用于 Web 服务的调用过程中。
  • 我们包括了一些代码在处理 Invoke Service 按钮的 Click 事件中,用来验证 “Enter your name...” 文本框的输入值,这样的话,如果这个文本框是空白的话,就不能调用 Web 服务。它的目的是避免使用空子符串调用 Web 服务而出现空指针异常。文本框的验证代码如 清单 1

清单1. 文本框验证
If TextBox1.Text <> "" Then   ...  Web Service invocation code goes here ...Else   MessageBox.Show("You must enter a name...", "User Input Error",    MessageBoxButtons.OK, MessageBoxIcon.Stop)   TextBox1.Focus()End If

考虑到 Web 服务调用的时间耗费,在做任何调用之前,执行一个基本的输入验证是很有意义的。

现在,我们有了一个用来调用 Sample1WebService 的基本的客户端 GUI,。我们现在需要编写用来调用 Web 服务及处理返回结果的代码,。





回页首

引用和调用 Web 服务

我们假定 WSDK 运行期服务器已经启动,WSDK Sample1WebService 已经运行并且启动(参阅文档,获得关于执行这个任务的细节)。

在 Microsoft Development Environment 的 Solution Explorer 窗口,右键单击 Sample1WebService 项目名称,选择 Add Web Reference... ,然后,在显示的对话框中,在 URL 域中输入 WSDK Sample1WebService 的 WSDL 文件地址,即:

http://localhost:6080/Sample1WebService/services/Sample1?wsdl

输入如 图2


添加新的 Web 引用面板

一旦您输入了 WSDL 文档的 URL 并单击 Go 按钮,IDE 会阅读文档,并且显示该 Web 服务暴露的方法, getGreeting() ,这个方法使用一个字符串作为参数,返回一个字符串作为结果。单击 Add Reference 按钮,确认客户端应用程序的引用。

我们需要做的下一个步骤就是:声明一个变量,当一个用户单击 Button1 (按钮的标签是 Invoke Service!)时调用 Sample1WebService 。在  Microsoft Development Environment 中选择浏览 Class View,它现在包含了 Sample1WebService 的一个树状描述。

展开您在这个视图中看到的 Sample1WebService 类,后面跟着 {}Sample1WebService、}localhost and {}Sample1InterfaceService,这样您就可以看到一个完全展开的树,如 图 3.


Sample1WebService 的 Class 树状视图

Invoke Service按钮的 Click 事件的代码窗口中输入如 清单2的声明,。

清单2. 调用 Servicebutton Click 事件
Dim WS As New Sample1WebService.localhost.Sample1InterfaceService(HostTextBox.Text)

或者输入 清单3中的代码,并从 Class View 窗口中单击和拖曳这个 Sample1InterfaceService 图标到代码窗口的前面几行。然后,添加 HostTextBox.Text 参数,作为来自第一个文本框的主机名称引用。这个文本框默认值是 localhost ,当然用户也可以自己编辑,以指向其他合适的机器。

清单3. 可替换代码
Dim WS As New

然后,在一个 try/catch 块中调用 Web 服务,如 清单4。这个代码使用 Enter Your name 文本框的内容来调用 Web 服务。

清单4. Try/catch 块
Try   TextBox2.Text = WS.getGreeting(TextBox1.Text)   Button1.Text = "&Clear"Catch ex As Exception   MessageBox.Show("A problem occurred executing the call to the Web Service",   "Application Server Error", MessageBoxButtons.OK, MessageBoxIcon.Error)End Try

您可能从面板设计中想起,我们允许用户指定一个不同于默认值 localhost 的其他主机名。为了能这样做,您需要进入 Microsoft Development Environment 的 Class View (如 图3),并展开 Sample1WebService ,然后是 localhost 和 Sample1InterfaceService 。双击 New(ByVal String) 方法图标。这将打开一个代码编辑器,光标定位在相应的 New() 方法上。然后,在现有的 MyBase.New() 调用下,添加如 清单5中的代码,该代码允许在用户输入的主机上初始化这个 Web 服务。

清单5. 初始化 Web 服务
REM Allow the user to specify a remote host for the Web ServiceDim lhost As StringIf host = "" Or host = "localhost" Then   lhost = "localhost"Else   lhost = hostEnd IfMe.Url = "http://" + lhost + ":6080/Sample1WebService/services/Sample1"

最后,为了提高程序可用性,我们在 Web 服务被调用时将 Button1 的文本改变为 Clear 。然后在 Button1 的 Click 事件处理的开头,我们检查它显示的文本,如果是 Clear ,我们清除文本框所有的文本,然后设置按钮的文本为 Invoke Service

Button1 的 Click 处理的完整代码如 清单6

清单6. Button1 的 Click 处理的完整代码  
If Button1.Text = "&Invoke Service" Then   REM Avoid null pointer exception by ensuring user has input a value   If TextBox1.Text <> "" Then      Dim WS As New Sample1WebService.localhost.Sample1InterfaceService(HostTextBox.Text)      Try         TextBox2.Text = WS.getGreeting(TextBox1.Text)         Button1.Text = "&Clear"      Catch ex As Exception    MessageBox.Show("A problem occurred executing the call to the Web Service",    "Application Server Error", MessageBoxButtons.OK, MessageBoxIcon.Error)      End Try   Else      MessageBox.Show("You must enter a name...", "User Input Error",      MessageBoxButtons.OK, MessageBoxIcon.Stop)      TextBox1.Focus()   End IfElseIf Button1.Text = "&Clear" Then   TextBox1.Text = ""   TextBox2.Text = ""   Button1.Text = "&Invoke Service"   TextBox1.Focus()End If

然后,当客户端应用程序保存完并启动后,将显示如 图4


客户端应用程序运行




回页首

开发一个更复杂的 Web 服务客户端

文章的这一部分扩展了在前面案例中学习的内容。客户端将启动 Web 服务,该 Web 服务将接收一个字符参数,搜索一个地址薄,然后如果有与参数字符串匹配的结果,返回一个名为 AddressBook 复杂类型(不过很明显,在真正的实现中,额外的业务逻辑和/或参数是必需的,然而对于要实现本文的目的,只用一个字符串将显得更简单)。

首先让我们看看将运行在 WSDK 服务器上的 Java 平台 Web 服务。它包含一个主要的服务类 AddressBookBean清单7),其中只有一个方法 getAddressFromName。这个方法使用了一个复杂数据类型 Address ( 清单8),其中包括数据类型 Phone( 清单9) 和 StateType( 清单10)。

清单7. AddressBookBean 类
package com.ibm.wsdk.demos.addr;import java.util.Hashtable;import java.util.Map;public class AddressBookBean {    private Map addresses = new Hashtable();    public AddressBookBean()    { // populate the AddressBook        // Andy        Address addrAndy = new Address();        Phone phoneNumberAndy = new Phone();        addrAndy.setStreetNum(57);        addrAndy.setStreetName("Mount Pleasant Street");        addrAndy.setCity("Metropolis");        addrAndy.setState(StateType.IN);        addrAndy.setZip(47907);        phoneNumberAndy.setAreaCode(765);        phoneNumberAndy.setExchange("555");        phoneNumberAndy.setNumber("4901");        addrAndy.setPhoneNumber(phoneNumberAndy);        addresses.put("andy", addrAndy);        // Nick        Address addrNick = new Address();        Phone phoneNumberNick = new Phone();        addrNick.setStreetNum(64);        addrNick.setStreetName("Zoo Lane");        addrNick.setCity("BigSmoke");        addrNick.setState(StateType.OH);        addrNick.setZip(67559);        phoneNumberNick.setAreaCode(881);        phoneNumberNick.setExchange("560");        phoneNumberNick.setNumber("9675");        addrNick.setPhoneNumber(phoneNumberNick);        addresses.put("nick", addrNick);    /**     * Retrieve an entry from the AddressBook.     *      *@param name the name of the entry to look up.     *@return the AddressBook entry matching name or null if none.     */    public Address getAddressFromName(java.lang.String name)     {        return(Address) this.addresses.get(name.toLowerCase());    }}

清单8. Address 类
package com.ibm.wsdk.demos.addr;public class Address implements java.io.Serializable {    private int streetNum;    private java.lang.String streetName;    private java.lang.String city;    private StateType state;    private int zip;    private Phone phoneNumber;    public Address() {    }    public Address(int streetNum,       java.lang.String streetName,       java.lang.String city,       StateType state,       int zip,       Phone phoneNumber) {        this.streetNum = streetNum;        this.streetName = streetName;        this.city = city;        this.state = state;        this.zip = zip;        this.phoneNumber = phoneNumber;    }    public int getStreetNum() { return streetNum; }    public void setStreetNum(int streetNum) {        this.streetNum = streetNum;    }    public java.lang.String getStreetName() { return streetName; }    public void setStreetName(java.lang.String streetName) {        this.streetName = streetName;    }    public java.lang.String getCity() { return city; }    public void setCity(java.lang.String city) {        this.city = city;    }    public StateType getState() { return state; }    public void setState(StateType state) {        this.state = state;    }    public int getZip() { return zip; }    public void setZip(int zip) {        this.zip = zip;    }    public Phone getPhoneNumber() { return phoneNumber; }    public void setPhoneNumber(Phone phoneNumber) {        this.phoneNumber = phoneNumber;    }}



清单9. Phone 类
package com.ibm.wsdk.demos.addr;public class Phone implements java.io.Serializable {    private int areaCode;    private java.lang.String exchange;    private java.lang.String number;    public Phone() {    }    public Phone(int areaCode, java.lang.String exchange, java.lang.String number) {        this.areaCode = areaCode;        this.exchange = exchange;        this.number = number;    }    public int getAreaCode() { return areaCode; }    public void setAreaCode(int areaCode) {        this.areaCode = areaCode;    }    public java.lang.String getExchange() { return exchange; }    public void setExchange(java.lang.String exchange) {        this.exchange = exchange;    }    public java.lang.String getNumber() { return number; }    public void setNumber(java.lang.String number) {        this.number = number;    }}



清单10. StateType 类
package com.ibm.wsdk.demos.addr;public class StateType implements java.io.Serializable {    private java.lang.String _value_;    private static java.util.HashMap _table_ = new java.util.HashMap();    // Constructor    protected StateType(java.lang.String value) {        _value_ = value;        _table_.put(_value_,this);    };    public static final java.lang.String _TX = "TX";    public static final java.lang.String _IN = "IN";    public static final java.lang.String _OH = "OH";    public static final StateType TX = new StateType(_TX);    public static final StateType IN = new StateType(_IN);    public static final StateType OH = new StateType(_OH);    public java.lang.String getValue() { return _value_;}    public static StateType fromValue(java.lang.String value)          throws java.lang.IllegalStateException {        StateType enum = (StateType)            _table_.get(value);        if (enum==null) throw new java.lang.IllegalStateException();        return enum;    }    public static StateType fromString(String value)          throws java.lang.IllegalStateException {        return fromValue(value);    }    public boolean equals(Object obj) {return (obj == this);}    public int hashCode() { return toString().hashCode();}    public String toString() { return _value_;}}

为了获得运行在 WSDK 服务器上的 AddressBookBean Web 服务,您需要将这四个 Java 类放到 WSDK 的一个合适目录中—— 注意:这个代码的 Java 包名为 com.ibm.wsdk.demos.addr ,因此您需要使用结尾为 com//ibm//wsdk//demos//addr 的目录名。使用 javac 编译这些代码,然后为 AddressBookBean 类运行 WSDK Bean2WebService 工具,并在运行时指定 deploy 选项,以便这个 Web 服务能部署到运行时服务器。

用于 AddressBookBean Web 服务的 WSDL 文件如 清单11

清单11. 用于 AddressBookBean Web 服务的 WSDL
<?xml version="1.0" encoding="UTF-8"?><wsdl:definitions targetNamespace="http://addr.demos.wsdk.ibm.com"   xmlns="http://schemas.xmlsoap.org/wsdl/"   xmlns:apachesoap="http://xml.apache.org/xml-soap"   xmlns:impl="http://addr.demos.wsdk.ibm.com"   xmlns:intf="http://addr.demos.wsdk.ibm.com"   xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"   xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"   xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <wsdl:types>  <schema elementFormDefault="qualified" targetNamespace=  "http://addr.demos.wsdk.ibm.com"     xmlns="http://www.w3.org/2001/XMLSchema">   <element name="getAddressFromName">    <complexType>     <sequence>      <element name="name" nillable="true" type="xsd:string"/>     </sequence>    </complexType>   </element>   <complexType name="Address">    <sequence>     <element name="streetNum" type="xsd:int"/>     <element name="streetName" nillable="true" type="xsd:string"/>     <element name="city" nillable="true" type="xsd:string"/>     <element name="state" nillable="true" type="impl:StateType"/>     <element name="zip" type="xsd:int"/>     <element name="phoneNumber" nillable="true" type="impl:Phone"/>    </sequence>   </complexType>   <simpleType name="StateType">    <restriction base="xsd:string">     <enumeration value="TX"/>     <enumeration value="IN"/>     <enumeration value="OH"/>    </restriction>   </simpleType>   <complexType name="Phone">    <sequence>     <element name="areaCode" type="xsd:int"/>     <element name="exchange" nillable="true" type="xsd:string"/>     <element name="number" nillable="true" type="xsd:string"/>    </sequence>   </complexType>   <element name="getAddressFromNameResponse">    <complexType>     <sequence>      <element name="getAddressFromNameReturn" nillable="true" type="impl:Address"/>     </sequence>    </complexType>   </element>  </schema> </wsdl:types>   <wsdl:message name="getAddressFromNameRequest">      <wsdl:part element="intf:getAddressFromName" name="parameters"/>   </wsdl:message>   <wsdl:message name="getAddressFromNameResponse">      <wsdl:part element="intf:getAddressFromNameResponse" name="parameters"/>   </wsdl:message>   <wsdl:portType name="AddressBookBean">      <wsdl:operation name="getAddressFromName">         <wsdl:input message="intf:getAddressFromNameRequest" name=         "getAddressFromNameRequest"/>         <wsdl:output message="intf:getAddressFromNameResponse" name=         "getAddressFromNameResponse"/>      </wsdl:operation>   </wsdl:portType>   <wsdl:binding name="AddressBookBeanSoapBinding" type=   "intf:AddressBookBean">      <wsdlsoap:binding style="document" transport=      "http://schemas.xmlsoap.org/soap/http"/>      <wsdl:operation name="getAddressFromName">         <wsdlsoap:operation soapAction=""/>         <wsdl:input name="getAddressFromNameRequest">            <wsdlsoap:body use="literal"/>         </wsdl:input>         <wsdl:output name="getAddressFromNameResponse">            <wsdlsoap:body use="literal"/>         </wsdl:output>      </wsdl:operation>   </wsdl:binding>   <wsdl:service name="AddressBookBeanService">      <wsdl:port binding="intf:AddressBookBeanSoapBinding" name=      "AddressBookBean">         <wsdlsoap:address location=         "http://localhost:6080/AddressBook/services/AddressBookBean"/>      </wsdl:port>   </wsdl:service></wsdl:definitions>

Microsoft .Net 工具可以使用如下 URL 访问 WSDL:

http://localhost:6080/AddressBook/services/AddressBookBean

在 Microsoft Visual Basic .Net 环境中,您要为 AddressBookBean Web 服务的客户端应用程序创建一个新的项目。在 Solution Explorer Window 中使用 AddressBookBean Web 服务的 WSDL URL 来添加一个对 AddressBookBean Web 服务的 Web 引用。这将为 AddressBookBean Web 服务创建一个 Class View, 如 图5


AddressBookBean Web 服务的 Class View

在 Class View,您不仅可以看到调用 Web 服务的方法,包括 getAddressfromName(...),还可以看到复杂类型 Address、 Phone StateType 的定义。注意:这些复杂类型等同于 Java 平台 Web 服务代码中的类型 —— 但是,Microsoft 工具会从使用 WSDL 文件构建它的 .Net 版本,而不是从 Java 代码中直接获得所有信息。

对于 Microsoft .Net 客户端,您要开发一个客户端应用程序窗体,其中包括文本框和按钮,如 图6 。在顶部是一个用户能输入名称来搜索电话薄的文本框。窗体的下面部分包含了一系列域,用来显示 AddressBookBean Web 服务返回的地址簿详细信息。


复杂 Web 服务的客户端

Search 按钮背后的初始逻辑是类似于第一个实例:我们在调用 Web 服务之前,检查文本框是不是一个空字符串。

您需要为 Web 服务声明和初始化一个变量,如 清单12 。您可以使用 Class View 窗口来帮助完成这一工作。

清单12. 为 Web 服务声明和初始化变量
Dim AddressService As New WebService.localhost.AddressBookBeanService

在这里实例中,我们也需要声明和初始化一个变量,用来处理 Web 服务返回的 Address 复杂类型,如 清单13

清单13. 声明和初始化一个变量来处理 Address
Dim Address1 As WebService.localhost.Address

然后我们调用 Address Book Web 服务,并且使用 Address 变量来保存匹配返回的复杂类型数据,如 清单14

清单14. 调用 Web 服务
REM Call the web service and get the details if there's a matchAddress1 = AddressService.getAddressFromName(TextBox1.Text)

下面要做的所有工作就是在客户端窗口显示这些域,可以使用 Web 服务返回的数据来设置它们的 Text值,如 清单15

清单15. 更新域的值
TextBox2.Text = Address1.streetNumTextBox3.Text = Address1.streetNameTextBox4.Text = Address1.cityTextBox5.Text = Address1.state.ToStringTextBox6.Text = Address1.zipREM Now handle the telephone number as complex typeTextBox7.Text = Address1.phoneNumber.exchangeTextBox8.Text = Address1.phoneNumber.areaCodeTextBox9.Text = Address1.phoneNumber.number

要相对注意的点是:使用 ToString 方法来获取 state属性的方式,以及 phoneNumber 属性元素的引用方式。

为了显得完整,客户端应用程序的 Button1 的 Click事件处理程序的完整代码如 清单16

清单16. 完整代码
REM Depending on the current text displayed on the button, take appropriate actionIf Button1.Text = "&Search" Then   REM Avoid a null pointer exception by not searching for an empty string   If TextBox1.Text <> "" Then      Try         Dim AddressService As New WebService.localhost.AddressBookBeanService         Dim Address1 As WebService.localhost.Address         REM Call the web service and get the details if there's a match         Address1 = AddressService.getAddressFromName(TextBox1.Text)         REM Populate the address fields on the form         TextBox2.Text = Address1.streetNum         TextBox3.Text = Address1.streetName         TextBox4.Text = Address1.city         TextBox5.Text = Address1.state.ToString         TextBox6.Text = Address1.zip         REM Now handle the telephone number as complex type         TextBox7.Text = Address1.phoneNumber.exchange         TextBox8.Text = Address1.phoneNumber.areaCode         TextBox9.Text = Address1.phoneNumber.number         REM Change the button caption to the next task         Button1.Text = "&Clear"      Catch ex As Exception         MessageBox.Show("An error occurred executing the Web Service",         "Application Server Error", MessageBoxButtons.OK, MessageBoxIcon.Error)      End Try   Else      MessageBox.Show("You must enter a name to search for", "No Name Supplied",      MessageBoxButtons.OK, MessageBoxIcon.Exclamation)      TextBox1.Focus()   End IfElseIf Button1.Text = "&Clear" Then   REM Do the obvious!   TextBox1.Text = ""   TextBox2.Text = ""   TextBox3.Text = ""   TextBox4.Text = ""   TextBox5.Text = ""   TextBox6.Text = ""   TextBox7.Text = ""   TextBox8.Text = ""   TextBox9.Text = ""   Button1.Text = "&Search"   TextBox1.Focus()End If

使用第二个 .Net 客户端应用程序调用我们的 Address Book Web 服务的执行结果如 图7所示。


调用 Web 服务之后的客户端应用程序




回页首

总结

在本文中,我们创建了一个简单的 Hello World Web 服务的 .Net 客户端,这个 Web 服务接收一个字符串作为参数,并返回另外一个字符串给客户端。我们研究了如何在 Microsoft Development Environment 中使用现有的 Web 服务,而且该 Web 服务运行在 WSDK 提供的应用服务器(Application Server)中。

然后,我们创建了第二个稍微复杂的 Web 服务 .Net 客户端,者个 Web 服务返回的数据类型是复杂类型。我们体会了如何从复杂类型中引用属性,这个复杂类型包含了另外的一个类(PhoneNumber)和一个枚举类型(StateType)。

在这个教程中的两个范例都采用了 Wrapped Doc/Lit WSDL 形式,以确保在基于 WebSphere 的 Web 服务和 .Net 客户端应用程序之间的互操作性。





参考资料

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文.

  • 您可以获得关于 WebSphere SDK for Web Services 的更多信息,包括下载包。



  • 您也可以获得关于 WebSphere Application Server 的更多信息。


原创粉丝点击