之前做.net,发布、引用webservice很容易,最近转做java,需要调用.net发布的webservice,于是网上找了几种方案,感觉很容易,实则遇到很多困难,放上来分享给大家。
WebService地址:http://localhost:8080/Service.asmx
请求SOAP:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<SecurityContext xmlns="http://tempuri.org/">
<UserName>string</UserName>
<Password>string</Password>
</SecurityContext>
</soap:Header>
<soap:Body>
<GetProject xmlns="http://tempuri.org/">
<strPrjCode>string</strPrjCode>
</GetProject>
</soap:Body>
</soap:Envelope>
响应SOAP:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetProjectResponse xmlns="http://tempuri.org/">
<GetProjectResult>string</GetProjectResult>
</GetProjectResponse>
</soap:Body>
</soap:Envelope>
其中有安全验证是重点,就是这个折腾我好久。请求需要用户名、密码,在SOAP Header里构造。
使用方案:axis2、jax-ws、url connection
一、axis2,用的比较多的方式,功能强大,需要了解详细资料,请自行百度。优点:使用简单,可发布、请求webservice,可以生成类文件,也会生成SOAP header节点安全验证类。缺点:比较重,如果只是请求webservice,不发布,也需要依赖很多第三方jar包。
axis2使用说明:
1、下载地址:http://archive.apache.org/dist/axis/axis2/java/core/1.6.2/axis2-1.6.2-bin.zip
2、生成类文件。axis2提供了一个生成类文件的工具,类似JDK提供的wsimport工具一样,可以根据webservice的WSDL生成类文件,使用这些类,可以像调用本地类一样调用webservice提供的服务。
cmd进入axis2-1.6.2\bin路径,执行wsdl2java -uri http://localhost:8080/Service.asmx?WSDL -p test.webservice -o stub -a -u Unpacks the databinding classes
参数说明:
-uri参数指定发布webservice地址。
-p指定生成的java类的包名。
-o指定生成一系列文件保存的根目录。
-a指定使用异步生成,默认同步生成。
-u Unpacks the databinding classes 避免生成的ServiceStub类特别大。
执行完,在当前目录下多了一个stub目录,可以看到ServiceStub.java,ServiceCallbackHandler.java文件,如果不加-a,不会有callbackHandler类。
3、使用,代码如下:
public String getProject(String projectCode, String userName, String password) {
try {
ServiceStub stub = new ServiceStub();
stub._getServiceClient().getOptions().setProperty(
org.apache.axis2.transport.http.HTTPConstants.CHUNKED,
Boolean.FALSE);
ServiceStub.SecurityContextE securityContextE = new ServiceStub.SecurityContextE();
ServiceStub.SecurityContext securityContext = new ServiceStub.SecurityContext();
securityContext.setUserName(userName);
securityContext.setPassword(password);
securityContextE.setSecurityContext(securityContext);
ServiceStub.GetProject getProject = new ServiceStub.GetProject();
getProject.setStrPrjCode(projectCode);
String result = stub.getProject(getProject, securityContextE).getGetProjectResult();
return result;
} catch (Exception e) {
logger.error(e.getMessage());
return null;
}
}
maven依赖ar包:
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-kernel</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-adb</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-transport-local</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-transport-http</artifactId>
<version>1.6.2</version>
</dependency>
二、jax-ws,JDK自带类库,无需应用第三方jar包,所以也是最后选择的原因。缺点是构造SOAP Header搞了好久,用wsimport生成的代码,需要修改才好用。不生成类文件,需要构造SOAP Header,不如axis2好用、易用。
首先介绍硬编码,不用生成类文件方式。直接上代码:
<span style="white-space:pre"> </span>private String userName;
private String password;
private static String ns = "http://tempuri.org/";
private static String wsdlUrl = "http://localhost:8080/Service.asmx?WSDL";
public String getProject(String projectCode, String userName, String password) {
this.userName = userName;
this.password = password;
try {
Map<String, String> args = new HashMap<>();
args.put("strPrjCode", projectCode);
String result = invoke("GetProject", args);
return result;
} catch (Exception e) {
logger.error(e.getMessage());
return null;
}
}
private SOAPMessage createSoapMessage(String userName, String password, String serviceName, Map<String, ?> args) {
try {
// 创建消息工厂
MessageFactory factory = MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
// 根据消息工厂创建SoapMessage
SOAPMessage message = factory.createMessage();
// 创建SOAPPart
SOAPPart part = message.getSOAPPart();
// 获取SOAPEnvelope
SOAPEnvelope envelope = part.getEnvelope();
// 通过SoapEnvelope可以获取到相应的Body和Header等信息
SOAPHeader header = envelope.getHeader();
// 根据Qname创建相应的节点,Qname是一个带有命名空间的节点
QName securityContext = new QName(ns, "SecurityContext");
SOAPHeaderElement headerElement = header.addHeaderElement(securityContext);
headerElement.addChildElement("UserName").setValue(userName);
headerElement.addChildElement("Password").setValue(password);
SOAPBody body = envelope.getBody();
QName function = new QName(ns, serviceName);
SOAPBodyElement bodyElement = body.addBodyElement(function);
for (String argName : args.keySet()) {
bodyElement.addChildElement(argName).setValue(args.get(argName).toString());
}
message.setProperty("SOAPAction", ns + serviceName);
message.saveChanges();
return message;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private String invoke(String serviceName, Map<String, ?> args) {
try {
// 创建服务service
URL url = new URL(wsdlUrl);
QName sname = new QName(ns, "Service");
Service service = Service.create(url, sname);
//创建DIspatch
Dispatch<SOAPMessage> dispatch = service.createDispatch(new QName(ns, "ServiceSoap12"),
SOAPMessage.class, Service.Mode.MESSAGE);
//创建SOAPMessage
SOAPMessage msg = createSoapMessage(this.userName, this.password, serviceName, args);
//通过Dispatch传递消息,会返回相应消息
SOAPMessage response = dispatch.invoke(msg);
//将相应的消息转换为doc对象
Document doc = response.getSOAPPart().getEnvelope().getBody().extractContentAsDocument();
String str = doc.getElementsByTagName(serviceName + "Response").item(0).getTextContent();
return str;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
注意:使用SOAPMessage构造SOAP Header,在构造时,一定要查看自己的应用webservice的SOAP格式,特别是namespace。
使用wsimport生成类文件方式:
1、在JDK的bin目录下,有wsimport.exe ,这个工具是根据wsdl文件生成相应的类文件。
如果jdk目录已经加入到环境变量,在cmd下执行如下命令:
wsimport -keep -p test.webservice http://localhost:8080/Service.asmx?WSDL
参数说明:
-d:生成客户端执行类的class文件的存放目录
-s:生成客户端执行类的源文件的存放目录
-p:定义生成类的包名
当前目录下会生成test目录,里面生成的文件列表是:
需要改动SecurityContext.java文件才能构造soap header。
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "SecurityContext", propOrder = {
"userName",
"password"
})
@XmlRootElement(name="SecurityContext")//添加这行,这行不会生成
public class SecurityContext {
@XmlElement(name = "UserName", namespace = "http://tempuri.org/")//namespace不会生成
protected String userName;
@XmlElement(name = "Password", namespace = "http://tempuri.org/")<span style="font-family: Arial, Helvetica, sans-serif;">//namespace不会生成</span>
protected String password;
@XmlAnyAttribute
private Map<QName, String> otherAttributes = new HashMap<QName, String>();
</pre><pre name="code" class="java">...
请求webervice:
public String getProject() {
try {
test.webservice.Service service1 = new test.webservice.Service();
ServiceSoap soap = service1.getServiceSoap();
WSBindingProvider provider = (WSBindingProvider) soap;
SecurityContext sc = new SecurityContext();
sc.setUserName(userName);
sc.setPassword(password);
provider.setOutboundHeaders(sc);
String result = soap.getProject(projectCode);
return result;
} catch (Exception e) {
logger.error(e.getMessage());
return null;
}
}
URL Connection我没有试,直接拼soap内容,太矬了,不过越是原始的东西,越容易做,不会像上面两种方案,碰到好多坑,解决起来很头疼。