【IT168 技术文档】
网络服务的宗旨是使运行于各种硬件平台上由不同厂商开发的应用,能够通过共同的一组协议和数据格式包括 SOAP、UDDI和WSDL进行相互定位和通讯。本文说明微软.NET的网络服务如何与BEA平台进行互操作。我首先描述如 何在每个平台上创建网络服务定义语言(WSDL)文件,然后讨论如何采用WSDL文件调用外部平台上的网络服务。 最后,将演示每个平台强大的、非标准的特性如何被其它平台使用。
两个简单的网络服务
在最初的例子中,我们将创建两个非常相似的网络服务,一个基于.NET,另一个基于WebLogic Workshop。两个网 络服务都有一个简单的方法getHelloMessage,返回简单的Hello消息。在示例中,将演示两个平台的网络服务之 间如何相互调用。
下面是WebLogic Workshop网络服务的代码:
public class WLWExample
{
/** @jws:operation */
public String getHelloMessage()
{ return "Hello from WebLogic Workshop!"; }
}
下面是.NET网络服务的代码:
using System.ComponentModel;
using System.Web.Services;
namespace DotNetExample
{
public class Service1 : WebService
{
public Service1()
{ InitializeComponent(); }
private IContainer components = null;
private void InitializeComponent()
{}
protected override void Dispose( bool disposing )
{
if(disposing && components != null)
components.Dispose();
base.Dispose(disposing);
}
[WebMethod] public string getHelloMessage()
{ return "Hello from Visual Studio .NET!"; }
}
}
在编写调用这些网络服务的代码之前,需要先快速浏览一下网络服务互操作的工作原理。
网络服务互操作原理
网络服务互操作的核心是网络服务定义语言(WSDL). WSDL是一种基于XML的语言,使网络服务能够以平台中立的方 式发布其公开契约。WSDL用于描述由网络服务提供的方法,并描述这些方法能够产生和接收的消息。 WSDL是一种 通用格式,两个不同的网络服务产品能够通过WSDL描述其网络服务。
为了能够跨平台调用上述两个网络服务,必须首先为这两个网络服务生成WSDL文件。描述过程在WebLogic Workshop和Visual Studio .NET稍有差别,但都非常简单。
在WebLogic Workshop中创建WSDL
1. 在项目树(project tree)上,用右键点击您的网络服务并选择"Generate WSDL from JWS."(从JWS生成WSDL ),将从您的网络服务创建一个新WSDL文件。项目树将创建的WSDL作为网络服务的子项。
WebLogic Workshop对WSDL文件和CTRL文件将采用一种特殊的命名约定 (将在后面讨论)。假定您有一个网络服务 命名为Service.jws,那么此服务的WSDL文件将被命名为ServiceContract.wsdl,其CTRL文件将被命名为 ServiceControl.ctrl。
在Visual Studio .NET中创建WSDL
1. 按F5从菜单中选择Debug/Start在浏览器中打开网络服务;
2. 在Internet Explorer中按右键点击"Service ,Description"(服务描述)连接,并选择"Save Target As"(存储目标为),并选择一个以。wsdl结束的文件名 。
即使对于简单的网络服务,其WSDL文件依然非常复杂。幸运的是,采用网络服务无需对WSDL细节进行了解。感 兴趣的读者,可以在http://www.w3.org/TR/wsdl找到WSDL完整的规范。
从WebLogic Workshop调用.NET网络服务
无论WebLogic Workshop或者是Visual Studio .NET,都有其内部机制来使用WSDL或引用被调用网络服务。在 WebLogic Workshop中,必须创建服务控制(Service Control),在Visual Studio .NET中,相应的概念是网络 引用(Web Reference)。本节将讨论如何为WSDL文件创建服务控制(Service Control),用于调用底层的网络 服务。下一节将对网络引用(Web Reference)进行讨论。
首先,需要创建一个新的WebLogic Workshop网络服务作为客户端访问.NET 网络服务。结束以后,再对此网络访 问增加服务控制。
从WSDL创建服务控制
1. 在设计视窗中点击"Add Control" (增加控制)下拉菜单并选择"Service Control."(服务控制)
2. 在第一个正文框中输入控制的变量名。这个变量名将被用于在源代码中引用此控制。
3. 选择组合框选项"Create a service control from a WSDL" (从WSDL创建服务控制),在正文框中输入WSDL 路径,也可以使用"Browse"(浏览)按钮指定WSDL位置 。
4. 按"Create"(创建)按钮。
创建服务控制将在您的网络服务中增加一行类似下列的代码:
/** @jws:control */
DotNetExampleControl dotNetExample;
控制变量名应引起足够重视,因为这个名子将用于调用.NET网络服务。现在,可以添加一个方法使用服务控制 调用.NET 网络服务。这个调用看起来像下面的代码:
/** @jws:operation */
public String callDotNet()
{ return dotNetExample.getHelloMessage(); }
之后,您应该有一个类似下面的WebLogic Workshop网络服务:
public class WLWClient
{
/** @jws:control */
private DotNetExampleControl dotNetExample;
/** @jws:operation */
public String callDotNet()
{ return dotNetExample.getHelloMessage(); }
}
假设.NET网络服务正在运行,您应该能够运行这个网络服务,并激活callDotNet方法向.NET网络服务发送跨平台 调用。当激活这个方法时,将获得一个应答消息:.NET hello 消息:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >
<soap:Body >
<getHelloMessageResponse xmlns="http://tempuri.org/" >
<getHelloMessageResult >
Hello from VisualStudio.NET!
</getHelloMessageResult >
</getHelloMessageResponse >
</soap:Body >
</soap:Envelope >
从.NET调用WebLogic Workshop 网络服务
现在我们已经可以从WebLogic Workshop调用.NET网络服务,下一步将从.NET调用WebLogic Workshop 网络服务。 在WebLogic Workshop中,我们采用了服务控制来调用外部网络服务,在.NET中,我们将采用网络引用(Web Reference)。
从WSDL创建网络引用
1. 选择"Project"菜单,然后选择"Add Web Reference."(添加网络引用)
2. 这时将弹出一个对话框用于浏览网络服务。在地址正文输入框中,输入为WebLogic WorkshopIn网络服务创建 的WSDL文件的路径。
3. 选择"Add Reference"(添加引用) 按钮。
您选择已经为项目(project)添加了指向WebLogic Workshop 网络服务的网络引用。默认情况下,将命名为 WebReference1。采用Solution Explorer,能够看到引用已添加到项目中,在引用之下,可以看到引用相应的WSDL 。
像使用其它对象一样,您可以在C#中使用网络引用。首先,必须将引用输入(import)到源文件中, 只需在C#文 件中增加下列代码:
using DotNetClient.WebReference1;
一旦将引用输入到源文件中,就可以创建引用实例(instance),并使用它调用WebLogic Workshop 网络服务。 下列代码说明了C#服务的这个功能:
using System.ComponentModel;
using System.Web.Services;
using DotNetExample.WebReference1;
namespace DotNetClient
{
public class Service1 : WebService
{
public Service1()
{ InitializeComponent(); }
private IContainer components = null;
private void InitializeComponent() {}
protected override void Dispose( bool disposing )
{
if(disposing && components != null)
components.Dispose();
base.Dispose(disposing);
}
private WLWExample wlwExample = new WLWExample();
[WebMethod] public DataSet callWLW()
{ wlwExample.getHelloMessage(); }
}
}
当您接口callWLW 方法时,将收到来自WebLogic Workshop 网络服务的hello消息。
< ?xml version="1.0" encoding="utf-8" ? >
< string xmlns="http://tempuri.org/" >
Hello from WebLogic Workshop!
</string >
从.NET调用会话式网络服务
在WebLogic Workshop中采用会话方式非常简单并且透明。但是,如果希望从.NET调用会话网络服务,必须需要额 外的工作以确保会话功能正常运行。
当网络服务客户端调用会话方法时,WebLogic Workshop采用了会话关键字确定这次调用相关的会话。会话关键字 嵌入在向网络服务发送的SOAP消息头中。对开始(start)方法,消息头格式如下:
<StartHeader >
<conversationID > convID < /conversationID >
<callbackLocation > callback < /callbackLocation >
</StartHeader >
会话ID和回调定位均为可选项。如果未提供会话ID,WebLogic Workshop将为您自动生成一个会话ID。回调定 位 (callback location) 用于指定WebLogic Workshop应向何处发送回调消息。
对于后续方法,会话头具有下列格式:
<ContinueHeader >
<conversationID > convID </conversationID >
</ContinueHeader >
对于后续方法,必须指定会话ID,因为需要有一种方式告诉WebLogic Workshop,调用这个后续方法时,希望 加入哪个会话。回调定位不是必须的,它已经在起始方法中指定过。
为一个.NET网络服务增加SOAP头非常简单。会话网络服务的WSDL文件已经指定了起始和后续头的结构。所有.NET 已经知道如何进行格式化。当.NET创建网络引用时,它为每一个在WSDL中指定的SOAP头的引用中置入了一个字段 (域):
// A web reference to a conversational Web service.
private WLWExample wlwExample;
[WebMethod] public void callStartMethod()
{
wlwExample.StartHeaderValue = new WebReference1.StartHeader();
wlwExample.StartHeaderValue.conversationID = 齝onvID?
wlwExample.StartHeaderValue.callbackLocation = 齝allbackLoc?
wlwExample.startMethod();
}
本例程假设已经有一个名为WebReference1 网络引用包含了一个指向名为cWLWExample网络服务的引用,与前面的 例程相似。WLWExample有一个名为startMethod方法开始一个会话。您应该用您实际需要的会话ID和回调定位替换 字符串"convID" 和"callbackLoc" 。
为了能够调用后续的方法,可以使用类似的代码(用ContinueHeaderValue替换StartHeaderValue )。在WebLogic Workshop中使用.NET DataSets
DataSet 是微软的活动数据对象(Active Data object ),用于提供数据的更通用的包装。一个DataSet 将 其数据模型化为一组表,其结构与SQL的表格非常相近,一样具有特定字段集、特定类型和次序。, DataSet对象 提供了丰富的工具装载和操作数据。但是,针对这篇文章的目的 ,我们对这些工具的兴趣远小于对数据集序列化 机制的关注。
创建DataSet样例
开始时,我们将创建一个简单的DataSet,表示电话簿并逐渐增加相应的例子数据。然后,将DataSet 从网络服务 返回。请注意DataSet如何序列化成为XML格式。
[WebMethod] public DataSet getDataSet()
{
// 创建新数据集,并增加相应的表。
DataSet dataSet = new DataSet("MyDataSet");
DataTable table = dataSet.Tables.Add("PhoneBook");
// 创建表列(字段)。
DataColumn primaryKeyCol = table.Columns.Add("ID", typeof(int));
table.Columns.Add("Name", typeof(string));
table.Columns.Add("Telephone", typeof(string));
// 设置ID字段作为主关键字。
table.PrimaryKey = new DataColumn[] { primaryKeyCol };
// 创建假想数据。
table.LoadDataRow(new object[] {1, "Fred", "555.2145"}, true);
table.LoadDataRow(new object[] {2, "Bob", "555.6246"}, true);
table.LoadDataRow(new object[] {3, "Howard", "555.1125"}, true);
table.LoadDataRow(new object[] {4, "Stanley", "555.0932"}, true);
// 将数据返回给调用者。
return dataSet;
}
DataSet的概要非常简单。这里DataSet(数据集)中有一个表,我们称为"PhoneBook." (电话簿),表包含有三列 -一个ID列,作为主关键字,一个Name列和一个Telephone列。我们已向本DataSet中添加了一些样例数据。当调 用方法时,将返回下列XML:
<?xml version="1.0" encoding="utf-8"?>
<DataSet xmlns="http://tempuri.org/">
<xs:schema id="MyDataSet"
xmlns=""
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xs:element name="MyDataSet" msdata:IsDataSet="true">
<xs:complexType>
<xs:choice maxOccurs="unbounded">
<xs:element name="PhoneBook">
<xs:complexType>
<xs:sequence>
<xs:element name="ID" type="xs:int" />
<xs:element name="Name" type="xs:string"
minOccurs="0" />
<xs:element name="Telephone" type="xs:string"
minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
<xs:unique name="Constraint1" msdata:PrimaryKey="true">
<xs:selector xpath=".//PhoneBook" />
<xs:field xpath="ID" />
</xs:unique>
</xs:element>
</xs:schema>
<diffgr:diffgram
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
<MyDataSet xmlns="">
<PhoneBook diffgr:id="PhoneBook1" msdata:rowOrder="0">
<ID>1</ID>
<Name>Fred</Name>
<Telephone>555.2145</Telephone>
</PhoneBook>
<PhoneBook diffgr:id="PhoneBook2" msdata:rowOrder="1">
<ID>21</ID>
<Name>Bob</Name>
<Telephone>555.6246</Telephone>
</PhoneBook>
<PhoneBook diffgr:id="PhoneBook3" msdata:rowOrder="2">
<ID>3</ID>
<Name>Howard</Name>
<Telephone>555.1125</Telephone >
</PhoneBook >
<PhoneBook diffgr:id="PhoneBook4" msdata:rowOrder="3" >
<ID>4</ID >
<Name > Stanley</Name >
<Telephone > 555.0932</Telephone >
</PhoneBook >
</MyDataSet >
</diffgr:diffgram >
</DataSet >
DataSet有两部分XML编码。第一部分为概要,描述了XML编码的结构。第二部分DataSet的实际数据体。本文中, 我们将忽略概要部分,假定预先已知,而只集中精力于数据自身的解码。在更复杂的应用中,可能需要利用概要 提供的其它信息。
我们创建的表中数据被编码为更简单的格式,其元素按下列格式不断重复。
<PhoneBook>
<ID></ID>
<Name></Name>
<Telephone></Telephone>
</PhoneBook>
给定我们所知的特定XML形式,我们能够编写一个WebLogic Workshop 网络服务使用这个数据。
利用XML Map(视图)提取数据
利用WebLogic Workshop的XML映射技术,我们能够捕获数据并将其存储为Java对象以表示电话簿中的信息项。首 先,我们需要定义一个Java类型以承载电话簿信息项。下列代码能够满足这个要求:
public static class PhoneBookEntry
{
public int id;
public String name;
public String phoneNumber;
}
为了将接收到的XML进行解码,我们将采用XML视图。下列XML视图能够用来返回服务控制的视图,这些服务控 制用于调用.NET 网络服务从而返回DataSet。
/**
* @jws:return-xml xml-map::
* <getDataSetResponse xmlns="http://tempuri.org/">
* <getDataSetResult>
* <diffgr:diffgram
* xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
* <MyDataSet xmlns="">
* <PhoneBook xm:multiple="entry in return">
* <ID>{entry.id}</ID>
* <Name>{entry.name}</Name>
* <Telephone>{entry.telephone}</Telephone>
* </PhoneBook>
* </MyDataSet>
* </diffgr:diffgram>
* </getDataSetResult>
* </getDataSetResponse>
* ::
*/
public PhoneBookEntry[] getDataSet();
本映射应该添加到.NET网络服务的CTRL的getDataSet方法中。映射看起来有点复杂,实际上只是深入到.NET网 络服务返回的XML,并获取电话簿条目列表。然后,利用xm:multiple 属性将每个电话簿条目的数据与getDataSet 返回的数组元素绑定。
您可能已经注意到,本映射建议的XML与前面描述有所不同。不幸的是,WebLogic Workshop收到的SOAP消息与 .NET网络接口显示的XML看上去有差别。可以通过从WebLogic Workshop激活.NET网络服务方法来发现WebLogic Workshop 实际收到的XML消息,并采用测试查看.NET返回的XML消息。
结束语
在网络服务世界中,互操作具有重要的意义。WebLogic Workshop很容易与其它网络服务实现实现互操作。本文中 ,我们浏览了与.NET互操作需要的工作。但是,本文讨论的原则适用于能够生成有效WSDL的任何网络服务产品。