jax-ws
一、webservice请求的工作原理:
客户端——> 阅读WSDL文档 (根据文档生成SOAP请求) ——>发送到Web服务器——>交给WebService请求处理器——>处理SOAP请求——> 调用WebService——>生成SOAP应答 ——> Web服务器通过http的方式交给客户端
详细描述如下:
无论使用什么工具/语言编写的webservice服务端,都可以使用soap协议,通过HTTP来调用,调用原理相同:创建webservice服务端,将其暴露给web客户端。客户端阅读其wsdl文档,根据wsdl描述文档生成相应的soap请求信息。客户生成的soap请求信息会被嵌入到一个http请求并发送到部署webservice服务的web服务器(如IIS)。web服务器再将请求转发给webservice请求处理器。webservice请求处理器用于解析收到的soap请求,调用webservice服务,获取soap应答。web服务器得到soap应答后,再通过http应答的方式返回给客户端。
WSDL、SOAP和UDDI一起工作,支持webservice和Internet上的其它服务、应用程序和设备交互作用。UDDI提供了发布和定位web服务的功能,WSDL描述了web服务,SOAP服务提供了传输协议。
补充两个概念:
1、soap:简单地说是一个通过http来传输xml数据的协议。
2、WSDL:web服务的接口定义语言,使用xml来描述web服务的标准。描述了如何访问具体的接口,包括三个属性:
1)如何访问服务:和服务交互的数据格式以及必要的协议;
2)服务在哪里:协议的相关地址,比如url等信息;
3)服务具有哪些功能:webservice中提供了哪些可以调用的方法;
二、java中的三种webservice规范
java中共有三种webservice规范:JAXM&SAAJ、JAX-WS(JAX-RPC)、JAX-RS。
JAX-WS规范是一组xml web service的java API。
规范是一组API?
规范只是集成在java平台里边的API,这个API是用来实现不同风格的JWS的。人们把这一套API叫做web服务规范。因为它们的设计目标不一样,JAX-WS是为实现基于soap协议的web service提供的API,soap协议比较强大。而JAX-RS是为基于REST设计风格的web service提供的API。有了API,然后我们再结合一些框架就能够很轻松的实现web service。
支持的框架:
支持JAX-WS服务规范的框架有:
CXF,Axis,Xfire,结合java语言均可实现JAX-WS。
支持JAX-RS服务规范的框架有:
CXF:Xfire和Celtix的合并
Jersey:sun公司的JAX-RS参考实现
RESTEasy:JBoss的JAX-RS项目
Restlet:也许是最早的REST框架了,在JAX-ES之前就有了
三、重点内容两种不同风格的SOA(面向服务的体系结构)架构:JAX-WS && JAX-RS
补充;
REST服务:
URL定位资源,用HTTP动词(GET、POST、DELETE、DELETE )描述操作。简单来讲,就是可以用httprequest调用某个function。比如在浏览器里输入 www.chx.site/api/guesswhoisawesome,就会调用后台的某个function等到一个response(可以是json)。REST服务采用HTTP做传输协议,REST对于HTTP的利用分为以下两种:资源定位和资源操作。
资源定位:
REST要求对资源定位更加准确,如下:
非rest方式:http://ip:port/queryUser.action?userType=student&id=001
Rest方式:http://ip:port/user/student/001
REST方式表示互联网上的资源更加准确,但是也有缺点,可能目录的层级较多不容易理解。
资源操作:
利用HTTP的GET、POST、PUT、DELETE四种操作来表示数据库操作的SELETE、UPDATE、INSERT、DELETE操作。
比如:
查询学生方法:
设置HTTP的请求方法为GET,url如下:
http://ip:port/user/student/001
添加学生方法:
设置http的请求方法为PUT,url如下:
http://ip:port/user/student/001/张三/…
REST常用与资源定位,资源操作方式较少使用。REST是一种软件架构理念,现在被移植到web服务上,那么再开发web服务的时候,偏于面向资源的服务使用REST。REST简单易用,效率高,SOAP成熟度较高,安全性较好。
注意:REST不是webservice,JAX-RS只是将REST设计风格应用到web服务开发上。REST风格的webservice不采用soap传输,直接采用http传输,可以返回xml或json。
jaxb:java xml binding,是 JAX-WS和JAX-RS底层使用的对象与XML之间转换的工具。
JAX-WS:java TM API for XML-based WedService,是针对webservices。
JAX-RS:java TM API for RESTful webservices,是针对RESTful HTTP Service。
JAX-WS是以动词为中心,指定的是每次执行函数。是面向消息的,每次请求都需要指定请求的方法。(大力支持的厂商如BEA,IBM,MS基本都是开发工具厂商,没有开发工具来做webservice会很繁琐)
JAX-RS是以名词为中心,每次执行的时候指的是资源。是面向资源的。后来将网络上的东西当作一种资源,每次请求都是对该资源进行操作,比如对资源的增删改查。RESTFul是一种风格而不是一个协议。它的理念就是:网络上的所有事物都被抽象为资源,每个资源对应一个唯一的资源标识符。(大力支持的厂商如Google,Yahoo,亚马逊等都是服务运营厂商,REST简洁好用,又能满足绝大部分需求)
四、JAX-WS VS JAX-RS 对于REST风格的web服务
JAX-WS适用于基于XML的web服务,如SOAP。JAX-RS没有相同的限制。
JAX-WS通常面向服务器到服务器与定义良好的契约(WSDL)的交互,通常当服务和客户端来自不同的组。这是非常耗费资源的,因此对于网络或客户端设备能力不够理想的客户端到服务器交互是不可行的。
JAX-RS适用于客户端到服务器的交互,尽管服务器到服务器是可以的。由于它几乎没有服务义务,可以根据客户的需求进行调整。
JAX-RS API只提供代码优先的方法,而JAX-WS允许使用WSDL文件(通常推荐),代码优先(通常不推荐)和合同优先。
JAX-RS 2.0引入了客户端API,它是HTTPURLConnection的一个智能包装,具有更多的映射能力,JAX-WS也是一个包装器,但是它在参考实现中处理的数据只是XML。
JAX-RS具有创建API的优势,这些API可以更简单的在不同的浏览器和移动设备上创建和消化消息,即JSON结构。它没有引入信封的概念,并使用HTTP。它不引入加密或安全性,它使用HTTPS。
JAX-WS虽然在HTTPS上运行,但是使用WS-SecurityPolicy等为安全性提供了额外的补充。此外,使用WSDL可以确定合同,并且使用ESB(如DataPower)在应用程序之外进行验证。
然而,使用RESTFul服务API类似于像Ruby和Python这样的元编程,这会延迟运行时间的问题,因为双方没有达成一致的定义模式和技术上的强制执行。因此,我不建议在任何地方使用RESTFul服务,但是我会推荐使用它。如果我控制了双方,那么当您构建使用静态HTML/CSS/JS的web应用程序并与RESTFul服务器通信时,数据的交互就会变得简单便捷。
五、调用webservice服务
1、使用axis1调用webservice服务(org.apache.axis.client.Service)
基于Axis服务端的webservice客户端实现
2、使用axis2调用webservice服务
2.1应用RPC的方式进行远程调用(org.apache.axis2.rpc.client.RPCServiceClient)
2.2使用AXIS2插件生成客户端的方式调用(wsdl2java工具),使用wsdl2java把WSDL文件转成本地类,然后像本地类一样使用,即可。参看本人如下博 客: 使用axis1.4生成webservice的客户端代码
3、使用http以及Spring提供的方法进行调用
最近在项目开发中遇到一个问题,在这里记录一下:
最近遇到一个奇怪的webservice服务,他们提供的请求报文和返回报文均为soap格式,如下:
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:sys="http://sysinf.ngis.ai.com">
<soap:Header/>
<soap:Body>
<sys:selectOfflineReasonUseAccount>
<sys:xml>
<![CDATA[<root>
<msgHead>
<requestId>1139792697</requestId>
<requestTime>2017-10-23 12:33:00</requestTime>
<source>EMOS</source>
<target>IPOSS</target>
</msgHead>
<msgBody>
<msgType>selectOfflineReasonUseAccount</msgType>
<block>
<account>09038140196</account>
<areaCode>0903</areaCode>
</block>
</msgBody>
</root>]]>
</sys:xml>
</sys:selectOfflineReasonUseAccount>
</soap:Body>
</soap:Envelope>
务地址( http://..*.:8081/services/NEService.wsdl ) 在浏览器可以正常打开。
但是当 我使用soupUI以soap协议的方式打开他们提供的地址的时候,显示“ERROR load”。
后面我又尝试在soupUI中使用REST的形式打开,打开正常。
我是用普通的请求soap协议webservice服务端的代码去调用这个服务,http响应码返回500,和如下错误信息
<faultstring>Only SOAP 1.1 or SOAP 1.2 messages are supported in the system</faultstring>
从报错信息我们可以大概猜测出,错误出在soap调用版本上。
之后我又尝试使用请求rest协议webservice服务端的代码去调用,依旧没有成功,这次没有http响应码和任何错误信息的返回,日志显示“read timeout”。
接着我进行第五次尝试,使用AXIS2插件生成客户端的方式去调用,生成的客户端代码如下:
调用代码如下:
问题完美解决。
从上面的报错信息我们知道错误出在soap协议版本的问题上,于是我就换了一种思路,更改我代码中的soap协议版本,使用服务端可以接受的版本去调用,于是就出现了以下两种客户端调用方法:
public String sendWebServiceRequest(String requestXml, String wsdl,String targetNamespace, String method) throws ServiceException,
MalformedURLException, RemoteException {
logger.debug("requestXml ------------ " + requestXml);
String responseXml = "";
requestXml = xmlDel(requestXml).trim();
logger.debug("进去提供方法------:");
PostMethod postMethod = new PostMethod(wsdl);
byte[] b = requestXml.getBytes();
InputStream is = new ByteArrayInputStream(b, 0, b.length);
// RequestEntity re = new InputStreamRequestEntity(is, b.length, "text/xml; charset=UTF-8");
RequestEntity re = new InputStreamRequestEntity(is, b.length, "APPlication/soap+xml; charset=UTF-8");
postMethod.setRequestEntity(re);
// postMethod.addRequestHeader("SOAPAction", "");//正常的soap请求
postMethod.addRequestHeader("content-Type","application/soap+xml");//soap1.2
httpclient hh = new HttpClient();
try {
int resCode=hh.executeMethod(postMethod);
logger.debug("rescode >>> " + resCode);
responseXml = postMethod.getResponseBodyAsString();
responseXml = StringEscapeUtils.unescapeXml(responseXml);//对返回的字符串进行转义
postMethod.releaseConnection();//关闭连接
} catch (IOException e) {
e.printstacktrace();
}finally{
postMethod.releaseConnection();
}
logger.info("ResponseXml---------:" + responseXml);
return responseXml;
}
public String sendWebServiceRequest(String requestXml, String wsdl,String targetNamespace, String method) throws ServiceException,
MalformedURLException, RemoteException {
logger.debug("requestXml ------------ " + requestXml);
String responseXml = "";
requestXml = xmlDel(requestXml).trim();
logger.debug("进去提供方法------:");
PostMethod postMethod = new PostMethod(wsdl);
byte[] b = requestXml.getBytes();
InputStream is = new ByteArrayInputStream(b, 0, b.length);
RequestEntity re = new InputStreamRequestEntity(is, b.length, "text/xml; charset=UTF-8");
postMethod.setRequestEntity(re);
// postMethod.addRequestHeader("SOAPAction", "");//正常的soap请求
postMethod.addRequestHeader("Content-Type","text/xml");//soap1.1
HttpClient hh = new HttpClient();
try {
int resCode=hh.executeMethod(postMethod);
logger.debug("rescode >>> " + resCode);
responseXml = postMethod.getResponseBodyAsString();
responseXml = StringEscapeUtils.unescapeXml(responseXml);//对返回的字符串进行转义
postMethod.releaseConnection();//关闭连接
} catch (IOException e) {
e.printStackTrace();
}finally{
postMethod.releaseConnection();
}
logger.info("ResponseXml---------:" + responseXml);
return responseXml;
}
重点就在两处注释的地方,正常的soap协议的JAX-WS,可以用soapUI的soap格式进行调用,使用上面注释的代码行也能调用。然而这个webservice服务需要在请求实体和请求方法头中加入请求头验证:Content-Type:application/soap+xml。我查了一些资料,Content-Type:text/xml是soap1.1,而Content-Type:application/soap+xml是soap1.2。
我们可以使用soapUI来获取webservice的请求/响应报文,以及可以测试webservice服务端是否能正常调用,除了soapUI之外,myEclipse也可以实现这一功能:
通过上一步会在浏览器打开如下页面:
有了soap请求报文和响应报文,接下来我们就可以进行webservice客户端的开发和使用了。
SOAP提升:
1.目前WebService的协议主要有SOAP1.1和1.2。
2.两者的命名空间不同。
3.SOAP1.1版本与SOAP1.2版本在头信息上存在差异。
3.1.SOAP1.1存在SOAPAction的请求头。
3.2.SOAP1.2没有SOAPAction的请求头。
4.基于SOAP1.1生成的WSDL和基于SOAP1.2生成的WSDL也不一样。
5.在CXF中两种协议请求的方式也不一样。
5.1 soap1.1为content-Type:text/xm;charset=UTF-8
5.2 soap1.2为content-Type:application/soap+xml;charset=UTF-8
命名空间:
Soap1.1的命名空间:
xmlns:soap=“http://schemas.xmlsoap.org/soap/envelope/“
Soap1.2 命名空间:
xmlns:soap=”http://www.w3.org/2003/05/soap-envelope“
SOAP1.1的HTTP请求头:
POST /xe_cxf2.4_soap12_spring_web/ws/helloworldsoap12?wsdl HTTP/1.1
Content-Type: text/xml; charset=UTF-8
Accept: **
User-Agent: Apache CXF 2.4.0
cache-Control: no-cache
pragma: no-cache
Host: localhost:6767
Connection: keep-alive
Content-Length: 214
SOAP1.2的请求头:
POST /xe_cxf2.4_soap12_spring_web/ws/helloworldsoap12?wsdl HTTP/1.1
Content-Type: application/soap+xml; charset=UTF-8
Accept: /
User-Agent: Apache CXF 2.4.0
Cache-Control: no-cache
Pragma: no-cache
Host: localhost:6767
Connection: keep-alive
Content-Length: 214
SOAP1.1和1.2的WSDL文件的差别:
在定义Service部分差别如下:
Soap1.1是以:soap:address
定义。
Soap1.2是以:soap12:address
定义。
注意:jdk1.6不支持12形式的访问。
通过BindingType将项目转到1.2:
在类上面添加以下注解可以使用soap1.2的协议:
@BindingType(value=SOAPBinding.SOAP12HTTP_BINDING)
或在applicationcontext.xml中使用binding
<jaxws:binding>
<soap:soapBinding version="1.2" />
</jaxws:binding>