JAVA+AXIS客户端调用Asp.net Web Service过程中遇到的问题及解决方法
2008-05-28 14:08
1306 查看
背景:公司与某运营商合作,运营商提供了接口文档,在文档中规定了数据流是双向的,运营商和公司之间的通讯采用Web Service方式,双方互为客户端和服务器端。这次遇到的问题,就是运营商的客户端调用我公司服务端的Web Service时出现的情况。需要特别说明的是:运营商有几十家合作伙伴,所以客户端的代码是不能因为某一家合作伙伴而修改的,各合作伙伴的WEB SERVICE开发环境也不相同,大部分都是用JAVA语言开发,而我公司是用VS2005开发的。
过程:
涉及的接口文档部分如下:
用户数据同步(syncUserData):
运营商客户端采用:JAVA JDK 1.5+AXIS实现的Web Service客户端调用,并提供了具体的调试例子:
package smp.webservice.client;
import java.rmi.RemoteException;
import javax.xml.namespace.QName;
import javax.xml.rpc.ServiceException;
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
public class ServiceClient ...{
public int syncUserData(String Mobile, String SPID, String Service, Integer Action, String Time, String Desc, String terminal, String serviceEndPoint)
throws Exception ...{
Object result=null;
try ...{
Call call = this.invokeFunction("syncUserData", serviceEndPoint);
result=call.invoke(new Object[] ...{ Mobile, SPID, Service, Action, Time, Desc, terminal });
} catch (Exception e) ...{
throw e;
}
try...{
return ((Integer)result).intValue();
}catch(Exception e)...{
return Integer.parseInt(((String)result));
}
}
public Call invokeFunction(String operationName, String serviceEndPoint)
throws ServiceException ...{
Service service = ServiceInstance.getInstance();
Call call = (Call) service.createCall();
call.setTargetEndpointAddress(serviceEndPoint);
call.setOperationName(new QName(operationName));
return call;
}
/** *//**
* test the client method
*/
public static void main(String[] args) ...{
ServiceClient sc = new ServiceClient();
String endPoint = "http://127.0.0.1/WebTest/Service.asmx";
try ...{
int i=sc.syncUserData("13312345678", "3735127", "834621", new Integer(8), "20080101120000", "desc","9",endPoint);
System.out.println("result: " + i);
} catch (Exception e) ...{
e.printStackTrace();
}
}
}
其中endPoint的值是用于调用我本地的.net开发的WEB服务地址。
我用asp.net中的C#语言生成了WEBSERVICE,但JAVA客户端调用会报错,后用AXIS中自带的抓包工具Axis Tcp Monitor对客户端的调用(此时调用的是运营商供测试用的WEB SERVICE,JAVA语言的)进行了抓包,结果如下:
POST /axis/services/SPService HTTP/1.0
Content-Type: text/xml; charset=utf-8
Accept: application/soap+xml, application/dime, multipart/related, text/*
User-Agent: Axis/1.2
Host: 127.0.0.1:8081
Cache-Control: no-cache
Pragma: no-cache
SOAPAction: ""
Content-Length: 1208
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<syncUserData soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<arg0 xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">13312345678</arg0>
<arg1 xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">3735127</arg1>
<arg2 xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">834621</arg2>
<arg3 href="#id0"/>
<arg4 xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">20080101120000</arg4>
<arg5 xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">desc</arg5>
<arg6 xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">9</arg6>
</syncUserData>
<multiRef id="id0" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="soapenc:int" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">8</multiRef>
</soapenv:Body>
</soapenv:Envelope>
发现参数名称都是arg0、arg1-arg6,而且int型参数arg3是通过href属性来实现的。查资料后得知AXIS客户端调用.Net是需要设置SoapDocumentService参数的。最后经过反复实验,终于用下面的C#代码成功处理了此客户端的请求:
using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Web.Services.Description;
[WebService(Namespace = "www.microsoft.com")]
[SoapDocumentService(RoutingStyle = SoapServiceRoutingStyle.RequestElement)]
[WebServiceBinding(ConformsTo = WsiProfiles.None)]
public class Service : System.Web.Services.WebService
...{
public Service () ...{
//Uncomment the following line if using designed components
//InitializeComponent();
}
[SoapRpcMethod(Action = "syncUserData",RequestNamespace="")]
public int syncUserData([SoapElement("arg0")] string Mobile, [SoapElement("arg1")] string SPID,
[SoapElement("arg2")] string Service, [SoapElement("arg3")] int Action, [SoapElement("arg4")] string Time,
[SoapElement("arg5")] string Desc, [SoapElement("arg6")] string Terminal)
...{
//处理过程省略……
return 0;
}
}
代码中:
SoapDocumentService属性:主要是用于JAVA+AXIS客户端调用.net服务端时使用的,具体的原因,可以网上查到。
WebServiceBinding(ConformsTo = WsiProfiles.None):因为上面SoapDocumentService属性的设置,所以此WEBSERVICE已经不符合微软默认的“‘WSI 基本概要’1.1 版”。
SoapRpcMethod属性:主要是因为客户端为了调用不同合作伙伴的WEB SERVICE,所以在JAVA语句call.setOperationName(new QName(operationName));中只设置了方法名称,而没有设置命名空间。.net 服务端为了处理这种情况下的请求,必须在本属性中设置RequestNamespace=""。这里的处理是非常重要的!
[SoapElement("arg0")] ……:是对请求进行XML序列化处理,把实际请求中的参数名称arg0映射到我自己定义的名称Mobile中。
有人可能认为在WEB 服务开始阶段设置[WebService(Namespace = "")]不也可以让命名空间设为空吗?为什么要在SoapRpcMethod属性中设置。这是因为在这个案例中,必须用到SoapRpcMethod属性,而使用此属性后,就不能设置[WebService(Namespace = "")],不然调用时服务端就会报错。具体原因,我还不是十分了解,如果有高手能解释这个原因,我将拭目以待:)。
还有一个情况就是,在调试过程中经常会发现,因为参数中有一个int型的数据(请参考抓包数据),所以往往string型的数据都能读出来了,这个int型的数据却总是为0 (读不出正确的值),所以上面的c#代码中的SoapRpcMethod属性就是解决这个问题的。
过程:
涉及的接口文档部分如下:
用户数据同步(syncUserData):
Index | Parameter Name | Req | Type | Size | Description |
1 | Mobile | M | String | 21 | 用户号码 |
2 | SPID | M | String | 21 | 合作方标识 |
3 | Service | M | String | 21 | 业务代码 |
4 | Action | M | Integer | 4 | 用户操作 |
5 | Time | M | String | 14 | 时间戳 YYYYMMDDhhmmss |
6 | Desc | M | String | 255 | 原因描述 |
7 | Terminal | M | String | 4 | 终端类型 |
package smp.webservice.client;
import java.rmi.RemoteException;
import javax.xml.namespace.QName;
import javax.xml.rpc.ServiceException;
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
public class ServiceClient ...{
public int syncUserData(String Mobile, String SPID, String Service, Integer Action, String Time, String Desc, String terminal, String serviceEndPoint)
throws Exception ...{
Object result=null;
try ...{
Call call = this.invokeFunction("syncUserData", serviceEndPoint);
result=call.invoke(new Object[] ...{ Mobile, SPID, Service, Action, Time, Desc, terminal });
} catch (Exception e) ...{
throw e;
}
try...{
return ((Integer)result).intValue();
}catch(Exception e)...{
return Integer.parseInt(((String)result));
}
}
public Call invokeFunction(String operationName, String serviceEndPoint)
throws ServiceException ...{
Service service = ServiceInstance.getInstance();
Call call = (Call) service.createCall();
call.setTargetEndpointAddress(serviceEndPoint);
call.setOperationName(new QName(operationName));
return call;
}
/** *//**
* test the client method
*/
public static void main(String[] args) ...{
ServiceClient sc = new ServiceClient();
String endPoint = "http://127.0.0.1/WebTest/Service.asmx";
try ...{
int i=sc.syncUserData("13312345678", "3735127", "834621", new Integer(8), "20080101120000", "desc","9",endPoint);
System.out.println("result: " + i);
} catch (Exception e) ...{
e.printStackTrace();
}
}
}
其中endPoint的值是用于调用我本地的.net开发的WEB服务地址。
我用asp.net中的C#语言生成了WEBSERVICE,但JAVA客户端调用会报错,后用AXIS中自带的抓包工具Axis Tcp Monitor对客户端的调用(此时调用的是运营商供测试用的WEB SERVICE,JAVA语言的)进行了抓包,结果如下:
POST /axis/services/SPService HTTP/1.0
Content-Type: text/xml; charset=utf-8
Accept: application/soap+xml, application/dime, multipart/related, text/*
User-Agent: Axis/1.2
Host: 127.0.0.1:8081
Cache-Control: no-cache
Pragma: no-cache
SOAPAction: ""
Content-Length: 1208
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<syncUserData soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<arg0 xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">13312345678</arg0>
<arg1 xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">3735127</arg1>
<arg2 xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">834621</arg2>
<arg3 href="#id0"/>
<arg4 xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">20080101120000</arg4>
<arg5 xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">desc</arg5>
<arg6 xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">9</arg6>
</syncUserData>
<multiRef id="id0" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="soapenc:int" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">8</multiRef>
</soapenv:Body>
</soapenv:Envelope>
发现参数名称都是arg0、arg1-arg6,而且int型参数arg3是通过href属性来实现的。查资料后得知AXIS客户端调用.Net是需要设置SoapDocumentService参数的。最后经过反复实验,终于用下面的C#代码成功处理了此客户端的请求:
using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Web.Services.Description;
[WebService(Namespace = "www.microsoft.com")]
[SoapDocumentService(RoutingStyle = SoapServiceRoutingStyle.RequestElement)]
[WebServiceBinding(ConformsTo = WsiProfiles.None)]
public class Service : System.Web.Services.WebService
...{
public Service () ...{
//Uncomment the following line if using designed components
//InitializeComponent();
}
[SoapRpcMethod(Action = "syncUserData",RequestNamespace="")]
public int syncUserData([SoapElement("arg0")] string Mobile, [SoapElement("arg1")] string SPID,
[SoapElement("arg2")] string Service, [SoapElement("arg3")] int Action, [SoapElement("arg4")] string Time,
[SoapElement("arg5")] string Desc, [SoapElement("arg6")] string Terminal)
...{
//处理过程省略……
return 0;
}
}
代码中:
SoapDocumentService属性:主要是用于JAVA+AXIS客户端调用.net服务端时使用的,具体的原因,可以网上查到。
WebServiceBinding(ConformsTo = WsiProfiles.None):因为上面SoapDocumentService属性的设置,所以此WEBSERVICE已经不符合微软默认的“‘WSI 基本概要’1.1 版”。
SoapRpcMethod属性:主要是因为客户端为了调用不同合作伙伴的WEB SERVICE,所以在JAVA语句call.setOperationName(new QName(operationName));中只设置了方法名称,而没有设置命名空间。.net 服务端为了处理这种情况下的请求,必须在本属性中设置RequestNamespace=""。这里的处理是非常重要的!
[SoapElement("arg0")] ……:是对请求进行XML序列化处理,把实际请求中的参数名称arg0映射到我自己定义的名称Mobile中。
有人可能认为在WEB 服务开始阶段设置[WebService(Namespace = "")]不也可以让命名空间设为空吗?为什么要在SoapRpcMethod属性中设置。这是因为在这个案例中,必须用到SoapRpcMethod属性,而使用此属性后,就不能设置[WebService(Namespace = "")],不然调用时服务端就会报错。具体原因,我还不是十分了解,如果有高手能解释这个原因,我将拭目以待:)。
还有一个情况就是,在调试过程中经常会发现,因为参数中有一个int型的数据(请参考抓包数据),所以往往string型的数据都能读出来了,这个int型的数据却总是为0 (读不出正确的值),所以上面的c#代码中的SoapRpcMethod属性就是解决这个问题的。
相关文章推荐
- asp.net调用word的过程中出现的问题及其解决方法
- PHP5 在调用 JAVA WebService 时遇到的各种问题及解决方法(一)
- PHP5 在调用 JAVA WebService 时遇到的各种问题及解决方法(二)
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(6):服务器端和客户端数据类型的自动转换:复杂类型
- 最近在ArcGIS Engine开发中关于调用gp工具过程出现COM 组件的调用返回了错误 HRESULT E_FAIL 错误的解决方法 和 学习oracle中遇到的一些问题总结
- 我在安装TFS 2008的时候遇到的问题以及解决方法一windows 2003 iis中总是不出现ASP.NET 2.0
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(9):服务器端和客户端数据类型的自动转换:DataTable和DataSet
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(6):服务器端和客户端数据类型的自动转换:复杂类型
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(9):服务器端和客户端数据类型的自动转换:DataTable和DataSet
- ASP.NET MVC3开发中遇到问题以及解决方法
- PHP5 在调用 JAVA WebService 时遇到的各种问题及解决方法(一)
- asp.net 用户控件 调用js问题解决方法
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(10):服务器端和客户端数据类型的自动转换:以XML方式序列化数据、小结
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(5):服务器端和客户端数据类型的自动转换:基本类型和枚举类型
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(7):服务器端和客户端数据类型的自动转换:泛型集合类型
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(8):服务器端和客户端数据类型的自动转换:数组类型
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(4):异步通讯层生成的客户端代理类、使用HTTP GET进行调用
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法:服务器端和客户端数据类型的自动转换:复杂类型
- 续:Java Tomcat 中调用.net DLL的方法 - 实际部署中遇到的一些问题解决
- ASP.NET页面借助IFrame提交表单数据所遇到问题的解决方法分享