用Java调用.net的Web service

原文:http://www.zhaoxiangpeng.com/articles/use-java-to-call-dotnet-web-service.html

Web service可以提高interoperability,可以实现跨平台的应用……听起来不错。但真的做一下,还是有很多小陷阱。

下面是最近做的小例子,用Java Axis2作为客户端调用.net写的web服务。支持自定义的数据结构(这个不是那么简单……)。

准备:

1、下载axis2 ,注意,不要google “axis”,那个是旧版的,一定要google“axis2”,目前的最新版本是1.41。

2、Visual studio 2008,C#。 这个不用说什么了。

服务端:

在visual studio里写个hello world服务很简单,函数前加个[WebMethod]就可以。但是, 如果用到自定义的类,比如下面定义的person类作为GetUserInfoByPerson服务的参数:

view plaincopy to clipboardprint?

  1. namespace WebService1  
  2. {  
  3. public class Person  
  4.     {  
  5. public string IdentityNumber  
  6.         {  
  7. get { return m_IdentityNumber; }  
  8. set { m_IdentityNumber = value; }  
  9.         }  
  10. private string m_IdentityNumber;  
  11.     }  
namespace WebService1

{

    public class Person

    {

        public string IdentityNumber

        {

            get { return m_IdentityNumber; }

            set { m_IdentityNumber = value; }

        }

        private string m_IdentityNumber;

    }

}

这时要注意,直接按Ctrl+F5后生成的wsdl不包括Person的定义。

正确的做法是加一行
[SoapRpcMethod(Action = "http://tempurl.org/GetUserInfoByPerson", RequestNamespace = "http://tempurl.org", Use=SoapBindingUse.Literal)]

代码如下:

view plaincopy to clipboardprint?

  1. [WebService(Namespace = "http://tempurl.org/")]  
  2. public class Service1 : System.Web.Services.WebService  
  3. {  
  4. [WebMethod]  
  5. [SoapRpcMethod(Action = "http://tempurl.org/GetUserInfoByPerson", RequestNamespace = "http://tempurl.org", Use=SoapBindingUse.Literal)]  
  6. // 不加这一句,wsdl中就不生成Person类型
  7. public Person GetUserInfoByPerson(Person q)  
  8. {  
  9. Person p = new Person();  
  10. p.IdentityNumber = q.IdentityNumber+"123";  
  11. }  
[WebService(Namespace = "http://tempurl.org/")]

public class Service1 : System.Web.Services.WebService

{

[WebMethod]

[SoapRpcMethod(Action = "http://tempurl.org/GetUserInfoByPerson", RequestNamespace = "http://tempurl.org", Use=SoapBindingUse.Literal)]

// 不加这一句,wsdl中就不生成Person类型

public Person GetUserInfoByPerson(Person q)

{

Person p = new Person();

p.IdentityNumber = q.IdentityNumber+"123";

}

}

现在Ctrl+F5运行这个服务,假设地址是http://localhost:56765/Service1.asmx,那就可以在http://localhost:56765/Service1.asmx?WSDL里看到,Person的定义已经出现了。

客户端:

axis2有一个不错的quickstart教程。axis2目录下面samples\faulthandling这个例子值得参考,里面有详细的readme.txt和build.xml。

1、用axis2自带的wsdl2java工具生成代码框架:

%AXIS2_HOME%\bin\wsdl2java.bat -uri http://localhost:56765/Service1.asmx?WSDL -u -o <target dir>

这样会在target dir下生成一个目录,里面有现成的Person.java等代码。

2、写调用代码

在Eclipse里建好项目,加入axis2\lib目录下所有的jar包,把刚才生成的目录也拷进去。

然后可以写代码了:

view plaincopy to clipboardprint?

  1. package example;  
  2. import org.tempurl.*;  
  3. public final class MyClient {  
  4. public static void main(String[] args) {  
  5. try {  
  6.     Service1Stub service1Stub = new Service1Stub("http://localhost:8080/Service1.asmx");  
  7.     service1Stub._getServiceClient().getOptions().setProperty(org.apache.axis2.transport.http.HTTPConstants.CHUNKED, Boolean.FALSE);  
  8.     Service1 service1 = service1Stub;  
  9.     GetUserInfoByPerson req = new GetUserInfoByPerson();  
  10.     Person personReq = new Person();  
  11.     personReq.setIdentityNumber("123");  
  12.     req.setQ(personReq);  
  13.     GetUserInfoByPersonResponse response = service1.GetUserInfoByPerson(req);  
  14.     PersonE personResponse = response.getGetUserInfoByPersonResult();  
  15.     System.out.println("ID = " + personResponse.getIdentityNumber());  
  16.   } catch (Exception e) {  
  17.   e.printStackTrace();  
  18. }  
  19. }  
package example;

import org.tempurl.*;

public final class MyClient {

  public static void main(String[] args) {

  try {

    Service1Stub service1Stub = new Service1Stub("http://localhost:8080/Service1.asmx");

    service1Stub._getServiceClient().getOptions().setProperty(org.apache.axis2.transport.http.HTTPConstants.CHUNKED, Boolean.FALSE);

    Service1 service1 = service1Stub;

    GetUserInfoByPerson req = new GetUserInfoByPerson();

    Person personReq = new Person();

    personReq.setIdentityNumber("123");

    req.setQ(personReq);

    GetUserInfoByPersonResponse response = service1.GetUserInfoByPerson(req);

    PersonE personResponse = response.getGetUserInfoByPersonResult();

    System.out.println("ID = " + personResponse.getIdentityNumber());

  } catch (Exception e) {

  e.printStackTrace();

}

}

}

注意这一句!service1Stub._getServiceClient().getOptions().setProperty(org.apache.axis2.transport.http.HTTPConstants.CHUNKED, Boolean.FALSE);

不做这个设置,一开始我怎么都调用失败,Axis2报告说HTTP send recv出错(记不清楚错误信息了)。用tcpmon查看调用服务时的tcp传输,发现.net的web server根本不接受axis2的soap包。(btw,tcpmon是个不错的工具啊。)

google了很久发现原因是,axis2在做http传输时采用了“chunked”模式,而.net的web server不支持。

“axis中使用的是HTTP/1.0协议,而.NET和axis2使用的是HTTP/1.1协议,后两者的区别在于.NET未使用ns1的命名空间前缀打包SOAP请求,且axis2使用了Content-Encoding: chunked头。 所以必须在axis2中设置一下。”

总结:

web服务调用还是很麻烦的。除了上面列举的问题,还可能有soap协议版本1.1和1.2不兼容之类的细节问题。在调试这类问题时,tcpmon是必备工具。

刚才讨论了如何用java调.net服务。如果反过来,不知是否会简单一点。

最后,其实我一年前就写过axis2 1.3的代码……可是昨天,除了知道这件事可以用axis2做,所有的细节都忘光了。还是花半个小时记下来比较安全。

你可能感兴趣的:(web Service)