构建 Web 服务客户端:从 Java 到 C# 的实践指南
在当今的软件开发中,Web 服务扮演着至关重要的角色。它允许不同的应用程序之间进行通信和数据交换,为分布式系统的构建提供了强大的支持。本文将详细介绍如何构建一个 Web 服务的客户端,包括 Java 客户端和 C# 客户端,并探讨 Web 服务的互操作性问题。
1. 导入 WSDL 并生成 Java 类
首先,我们需要使用
WSDL2Java
工具将 WSDL 文件转换为 Java 类。以下是 Ant 脚本中的相关配置:
<target name="import-wsdl" depends="fetch-wsdl">
<java
classname="org.apache.axis.wsdl.WSDL2Java"
fork="true"
failonerror="true"
classpathref="axis.classpath"
>
<arg file="${local.wsdl}"/>
<arg value="--output"/>
<arg file="${generated.dir}"/>
<arg value="--verbose"/>
<arg value="--package"/>
<arg value="soapapi"/>
<arg value="--testCase"/>
</java>
</target>
运行
import-wsdl
目标后,
WSDLToJava
程序将在
${generated.dir}/soapapi
目录下生成以下文件:
-
SearchService.java
-
SearchServiceService.java
-
SearchServiceServiceLocator.java
-
SearchServiceServiceTestCase.java
-
SearchServiceSoapBindingStub.java
这些类构成了 Web 服务的定位器和代理,以及一个自动生成的 JUnit 测试用例类。我们可以使用这个生成的测试用例作为测试框架。
2. 实现测试用例
生成的 JUnit 测试用例仅测试使用一些参数调用端点的方法。例如,为
search
方法生成的测试发送一个空字符串作为搜索词,并且不处理返回值:
package soapapi;
public class SearchServiceServiceTestCase extends junit.framework.TestCase
{
public SearchServiceServiceTestCase(String name) {
super(name);
}
public void test1SearchServiceSearch() {
soapapi.SearchService binding;
try {
binding = new soapapi.SearchServiceServiceLocator().
getSearchService();
}
catch (javax.xml.rpc.ServiceException jre) {
throw new junit.framework.
AssertionFailedError("JAX-RPC ServiceException caught: " + jre);
}
assertTrue("binding is null", binding != null);
try {
java.lang.String[] value = null;
value = binding.search(new java.lang.String());
}
catch (java.rmi.RemoteException re) {
throw new junit.framework.
AssertionFailedError("Remote Exception caught: " + re);
}
}
}
为了创建真正的测试用例,我们需要完成以下三个步骤:
1.
复制生成的文件
:将生成的文件复制到我们的源代码树中,确保它不会被覆盖,并将其移出
soapapi
包,以避免编译时源文件覆盖
.class
文件。
2.
编辑测试方法
:编辑测试方法,向 SOAP 服务发送有效数据,并检查返回的有效数据。例如,对于
search
方法的测试,我们发送一个真实的搜索词并要求返回一个非空数组:
try {
java.lang.String[] value = null;
value = binding.search("test");
assertTrue("should have got an array back",
value!=null && value.length>0);
}
catch (java.rmi.RemoteException re) {
throw new junit.framework.
AssertionFailedError("Remote Exception caught: " + re);
}
-
运行测试
:编译测试和代理类,使用包含 Axis、Xerces 和 JUnit 的类路径,然后使用
<junit>任务调用测试。以下是 Ant 脚本中的相关配置:
<target name="test" depends="compile"
description="Execute unit tests">
<junit printsummary="yes"
errorProperty="test.failed"
failureProperty="test.failed"
fork="true">
<classpath>
<path refid="axis.classpath"/>
<pathelement location="${build.classes.dir}"/>
</classpath>
<batchtest>
<fileset dir="${client.src.dir}"
includes="**/*TestCase.java"/>
</batchtest>
</junit>
</target>
3. 编写 Java 客户端
测试通过后,我们可以编写真正的 Java 客户端:
import soapapi.*;
public class SearchClient {
public static void main(String args[]) throws Exception {
SearchServiceServiceLocator locator;
locator=new SearchServiceServiceLocator();
soapapi.SearchService service=locator.getSearchService();
String lastTerm=service.getLastSearchTerm();
System.out.println("last search = "+lastTerm);
String[] results=service.search(args[0]);
for(int i=0;i<results.length;i++) {
System.out.println(results[i]);
}
}
}
这个客户端有三个阶段:
1.
查找并绑定服务
:使用
SearchServiceServiceLocator
查找并绑定到服务。
2.
检索并显示上一个搜索词
:调用
getLastSearchTerm
方法检索并显示上一个搜索词。
3.
发送搜索词并打印结果
:将应用程序的第一个参数作为搜索词发送到 Web 服务,并打印结果。
为了运行这个程序,我们可以编写一个 Ant 目标来调用它:
<target name="run" depends="test">
<java
classname="SearchClient"
fork="true"
failonerror="true"
>
<arg value="deployment"/>
<classpath>
<path refid="axis.classpath"/>
<pathelement location="${build.classes.dir}"/>
</classpath>
</java>
</target>
4. 互操作性问题
互操作性是 SOAP 面临的一个持续问题。虽然 SOAP 工具包的开发者致力于互操作性测试,以验证基本数据类型(如字符串、整数、布尔值、数组和 Base64 编码的二进制数据)可以在客户端和服务器之间交换,但复杂类型尚未标准化。
例如,Java 和 .NET 都有自己的
HashTable
实现。如果我们在服务中返回一个
HashTable
,使用相同工具包的客户端可以正常调用该方法并获取哈希表,但使用其他工具包或不同语言的客户端将无法处理。这就是所谓的“哈希表问题”。
为了解决这个问题,我们需要在开发 Web 服务时更加谨慎,避免使用不可互操作的数据类型。如果可能的话,应该使用标准化的 XSD 描述所有数据类型,并编写清晰的名称 - 值对模式来表示复杂类型。
5. 构建 C# 客户端
为了早期检测互操作性问题,我们需要创建一个使用不同 SOAP 工具包的客户端,并验证它可以调用我们的服务。这里我们选择创建一个 C# 客户端。
5.1 探测类
由于我们只支持 Windows 上的 .NET 实现,因此需要确保系统中安装了 .NET SDK 并将其添加到 PATH 中。以下是 Ant 脚本中用于探测相关程序的配置:
<target name="probe_for_dotnet_apps" >
<condition property="wsdl.found">
<or>
<available file="wsdl" filepath="${env.PATH}" />
<available file="wsdl.exe" filepath="${env.PATH}" />
<available file="wsdl.exe" filepath="${env.Path}" />
</or>
</condition>
<echo> wsdl.found=${wsdl.found}</echo>
<condition property="csc.found">
<or>
<available file="csc" filepath="${env.PATH}" />
<available file="csc.exe" filepath="${env.PATH}" />
<available file="csc.exe" filepath="${env.Path}" />
</or>
</condition>
<echo> csc.found=${csc.found}</echo>
<condition property="dotnetapps.found">
<and>
<isset property="csc.found"/>
<isset property="wsdl.found"/>
</and>
</condition>
<echo> dotnetapps.found=${dotnetapps.found}</echo>
</target>
5.2 导入 WSDL 生成 C# 代码
使用
<wsdltodotnet>
任务将 WSDL 文件转换为 C# 代码:
<property name="out.csc" location="${generated.net.dir}/soapapi.cs"/>
<target name="import-dotnet" depends="probe_for_dotnet_apps,fetch-wsdl"
if="dotnetapps.found">
<wsdltodotnet destFile="${out.csc}"
srcFile="${local.wsdl}"
/>
</target>
5.3 编写 C# 客户端类
以下是 C# 客户端的代码:
using System;
public class DotNetSearchClient {
public static void Main(String[] args) {
SearchServiceService service=new SearchServiceService();
String lastTerm=service.getLastSearchTerm();
Console.WriteLine("last search = "+lastTerm);
String[] results=service.search(args[0]);
for(int i=0;i<results.Length;i++) {
Console.WriteLine(results[i]);
}
}
}
5.4 构建 C# 客户端
使用
<csc>
任务编译 C# 代码:
<property name="out.app" location="${build.net.dir}/netclient.exe"/>
<target name="build-dotnet" depends="import-dotnet"
if="dotnetapps.found">
<copy toDir="${generated.net.dir}">
<fileset dir="${src.net.dir}" includes="**/*.cs" />
</copy>
<csc
srcDir="${generated.net.dir}"
destFile="${out.app}"
targetType="exe"
>
</csc>
</target>
5.5 运行 C# 客户端
使用
<exec>
任务运行编译后的 C# 客户端:
<target name="dotnet" depends="build-dotnet" if="dotnetapps.found">
<exec
executable="${out.app}"
failonerror="true"
>
<arg value="deployment"/>
</exec>
</target>
6. C# 客户端构建过程总结
构建 C# 客户端的过程与构建 Java 客户端类似,主要包括以下步骤:
1.
探测类
:确保系统中安装了 .NET SDK 并将其添加到 PATH 中。
2.
导入 WSDL
:使用
<wsdltodotnet>
任务将 WSDL 文件转换为 C# 代码。
3.
编写客户端类
:编写 C# 客户端代码,调用 Web 服务。
4.
构建客户端
:使用
<csc>
任务编译 C# 代码。
5.
运行客户端
:使用
<exec>
任务运行编译后的 C# 客户端。
通过构建 C# 客户端,我们可以验证 Java 基于的 Web 服务与其他 SOAP 实现的互操作性。如果在开发过程中遇到复杂的 .NET 客户端,我们可以考虑使用 NAnt 或为 Ant 编写
<nunit>
任务。
7. 严谨的 Web 服务构建方法
最严谨的构建 Web 服务的方法是创建接口的 WSDL 规范,并可能使用 XSD 描述所有数据类型。此外,建议将服务实现为 Java 文件,而不是 JWS 页面,这样可以避免将 Java 源文件复制并重命名为 JWS 页面的过程。
虽然我们没有详细介绍这种更严谨的服务器端开发过程,但可以提供一些探索方向。例如,Axis CVS 树中的测试服务器类是服务生成的最新示例,可以作为学习的起点。
总之,构建 Web 服务客户端需要考虑多个方面,包括代码生成、测试、互操作性和严谨的开发方法。通过本文的介绍,希望读者能够更好地理解和实践 Web 服务客户端的构建。
构建 Web 服务客户端:从 Java 到 C# 的实践指南
8. 构建过程流程图
下面是 Java 客户端和 C# 客户端构建过程的 mermaid 流程图,帮助大家更直观地理解整个流程。
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;
A([开始]):::startend --> B{选择客户端类型}:::decision
B -->|Java| C(导入 WSDL 生成 Java 类):::process
C --> D(实现测试用例):::process
D --> E(编写 Java 客户端):::process
E --> F(运行 Java 客户端):::process
B -->|C#| G(探测类):::process
G --> H(导入 WSDL 生成 C# 代码):::process
H --> I(编写 C# 客户端类):::process
I --> J(构建 C# 客户端):::process
J --> K(运行 C# 客户端):::process
F --> L([结束]):::startend
K --> L
9. 关键步骤对比
为了更清晰地对比 Java 客户端和 C# 客户端的构建过程,我们可以用表格的形式呈现关键步骤:
| 步骤 | Java 客户端 | C# 客户端 |
| ---- | ---- | ---- |
| 代码生成 | 使用
WSDL2Java
工具根据 WSDL 文件生成 Java 类 | 使用
<wsdltodotnet>
任务将 WSDL 文件转换为 C# 代码 |
| 测试编写 | 编写 JUnit 测试用例,编辑测试方法发送有效数据并检查返回值 | 由于 Microsoft 工具包不自动生成 NUnit 测试,需手动编写测试 |
| 客户端编写 | 编写 Java 代码,使用生成的类查找并绑定服务,发送搜索词并处理结果 | 编写 C# 代码,调用生成的代理类方法,发送搜索词并处理结果 |
| 构建 | 使用
<javac>
编译 Java 源文件 | 使用
<csc>
任务编译 C# 源文件 |
| 运行 | 使用
<java>
任务运行 Java 程序 | 使用
<exec>
任务运行编译后的 C# 可执行文件 |
10. 操作步骤总结
无论是 Java 客户端还是 C# 客户端,构建 Web 服务客户端都有一些通用的操作步骤,我们可以总结如下:
1.
准备工作
- 确保所需的开发环境和工具已安装,如 Java 开发环境、.NET SDK 等。
- 下载并准备好 WSDL 文件。
2.
代码生成
- 根据 WSDL 文件生成客户端代码,这一步可以使用相应的工具或任务,如
WSDL2Java
或
<wsdltodotnet>
。
3.
测试编写
- 编写测试用例,验证服务的基本功能和数据交互的正确性。对于 Java 客户端,可使用 JUnit 框架;对于 C# 客户端,可考虑使用 NUnit 框架。
4.
客户端编写
- 编写客户端代码,调用生成的代理类方法,实现与 Web 服务的交互。
5.
构建和运行
- 编译客户端代码,生成可执行文件或类文件。
- 运行客户端程序,验证服务调用的结果。
11. 注意事项
在构建 Web 服务客户端的过程中,还需要注意以下几点:
-
互操作性
:如前文所述,复杂类型的互操作性是一个常见问题。在开发过程中,应尽量使用标准化的数据类型和模式,避免使用特定工具包或语言的独有特性。
-
文件管理
:在构建 C# 客户端时,由于
<csc>
任务只能指定一个源目录,需要将手写的源文件复制到生成的目录中。这可能会导致 IDE 中打开的是复制后的文件,而不是原始文件,需要特别注意。
-
环境依赖
:构建 C# 客户端需要 Windows 系统和 .NET SDK 的支持。在不同的环境中进行开发和测试时,需要确保环境的一致性。
12. 未来展望
随着 Web 服务技术的不断发展,互操作性问题有望得到更好的解决。未来可能会有更多标准化的规范和工具出现,使得不同语言和工具包之间的交互更加顺畅。
同时,自动化测试和持续集成的重要性也会日益凸显。通过建立完善的测试体系和自动化构建流程,可以提高开发效率和服务的稳定性。
总之,构建 Web 服务客户端是一个不断学习和实践的过程。希望本文能够为大家提供一些有用的参考和指导,帮助大家更好地应对 Web 服务开发中的挑战。
超级会员免费看
729

被折叠的 条评论
为什么被折叠?



