PHP5开发之WSDL,SOAP调用实现过程

一、基础概念

SOAP(Simple Object Access Protocol
)简单对象访问协议是在分散或分布式的环境中交换信息的简单的协议,是一个基于XML的协议,它包括四个部分:SOAP封装(envelop),封装定义
了一个描述消息中的内容是什么,是谁发送的,谁应当接受并处理它以及如何处理它们的框架;SOAP编码规则(encoding
rules),用于表示应用程序需要使用的数据类型的实例; SOAP RPC表示(RPC
representation),表示远程过程调用和应答的协定;SOAP绑定(binding),使用底层协议交换信息。

WSDL(Web Service Description Language)就是描述XML
Web服务的标准XML格式,WSDL由Ariba、Intel、IBM和微软等开发商提出。它用一种和具体语言无关的抽象方式定义了给定Web服务收发
的有关操作和消息。就其定义来说,你还不能把WSDL当作一种对象接口定义语言,例如,CORBA或COM等应用程序体系结构就会用到对象接口定义语言。
WSDL保持协议中立,但它确实内建了绑定SOAP的支持,从而同SOAP建立了不可分割的联系。所以,当我在这篇文章中讨论WSDL的时候,我会假定你
把SOAP作为了你的通讯协议。

SOAP和WSDL虽然是web
service的两大标准,但是两者并没有必然的联系,都可以独立使用。它们之间的关系就类似HTTP和Html之间的关系。前者是一种协议,后者是对一个Web
Server的描述。

Web
Service概述

什么是WebService?

二、PHP5下的配置

在php的的配置文件php.ini中,找到

extension=php_soap.dll

然后将前面的;号去掉,然后重启web服务

Web
Service的定义
W3C组织对其的定义如下,它是一个软件系统,为了支持跨网络的机器间相互操作交互而设计。Web
Service服务通常被定义为一组模块化的API,它们可以通过网络进行调用,来执行远程系统的请求服务。

它是一种构建应用程序的普遍模型,可以在任何支持网络通信的操作系统中实施运行;它是一种新的web
webservice应用程序分支,是自包含、自描述、模块
化的应用,可以发布、定位、通过web调用。Web
Service是一个应用组件,它逻辑性的为其他应用程序提供数据与服务.各应用程序通过网络协议和规定的一些标准数据格式(Http,XML,Soap)来访问Web
Service,通过Web Service内部执行得到所需结果.Web
Service可以执行从简单的请求到复杂商务处理的任何功能。一旦部署以后,其他Web
Service应用程序可以发现并调用它部署的服务。

可以将其理解为服务器通过特殊API,为其他用户提供服务的一种方式。它可以实现跨平台调度。

三、查询web service方法与参数、数据类型

某省电信公司的入单接口为

我们使用SoapClient的__geunctions()和__getTypes()方法查看该接口的方法,参数和数据类型只有__getFunctions中列出的接口才能被soap调用。

在根目录下创建代码soap.php

<?php
header("content-type:text/html;charset=utf-8");
try {
    $client = new SoapClient("http://***.******.com/services/AcceptedBusiness?wsdl");
    print_r($client->__getFunctions());
    print_r($client->__getTypes());  
} catch (SOAPFault $e) {
    print $e;
}
?>

在浏览器运行:

Array
(
    [0] => ArrayOf_xsd_anyType introduceAcceptedBusiness(string $c3, string $c4, string $linkman, string $linknum, string $num, string $idcard, string $remark, string $address)
    [1] => ArrayOf_xsd_anyType introduceAcceptedBusinessByAiZhuangWei(string $subname, string $linkphone, string $idcard, string $address, string $businesstype, string $marketcode, string $surveycode, string $commanager, string $commanagerphone, string $bendiwang, string $fenju, string $zhiju, string $remark)
    [2] => string introduceAcceptedBusinessByStandardInterface(string $xmlStr)
    [3] => string introduceAcceptedBusinessByCallOut(string $xmlStr)
    [4] => string introduceAcceptedBusinessByYddj(string $xmlParam)
    [5] => ArrayOf_xsd_anyType queryAcceptedBusinessByAiZhuangWei(string $surveycode, string $starttime, string $endtime)
    [6] => string queryCallOutOrderByConfig(string $xmlParam)
)
Array
(
    [0] => anyType ArrayOf_xsd_anyType[]
)

其中有个方法 introduceAcceptedBusinessByStandardInterface(string
$xmlStr),将是开发文档中提到的要使用的接口,参数为xml字符串

另外有的接口中提到有SoapHeader认证,这就需要加入__setSoapHeaders方法,具体可查看

这里我们从一个程序员的视角来观察web
service。在传统的程序编码中,存在这各种的函数方法调用。通常,我们知道一个程序模块M中的方法A,向其发出调用请求,并传入A方法需要的参数P,方法A执行完毕后,返回处理结果R。这种函数或方法调用通常发生在同一台机器上的同一程序语言环境下。现在的我们需要一种能够在不同计算机间的不同语言编写的应用程序系统中,通过网络通讯实现函数和方法调用的能力,而Web
service正是应这种需求而诞生的。

WebService是由哪几部分组成的?

四、提交入单

这一步就是需要根据开发文档拼接xml字符串,然后作为introduceAcceptedBusinessByStandardInterface的参数传入
创建acceptedbusiness.php,内容如下

<?php
header("content-type:text/html;charset=utf-8");
try {
    $client = new SoapClient('http://***.*******.com/services/AcceptedBusiness?wsdl');
    $xml = "
    <?xml version='1.0' encoding='UTF-8' ?>
    <PACKAGE>
      <C3>**电信</C3>
      <C4></C4>
      <LINKMAN>张三</LINKMAN>
      <LINKNUM>13412341234</LINKNUM>
      <LINKADDRESS>广东深圳</LINKADDRESS>
      <REMARK>iPhone 6</REMARK>
      <CHANNEL></CHANNEL>
      <GRIDCODE>1111111111111111111111111111111</GRIDCODE>
      <AGENTCODE>2111</AGENTCODE>
      <KEY>1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111</KEY>
    </PACKAGE>
  ";
    $return = $client->introduceAcceptedBusinessByStandardInterface($xml);
    print_r($return);
} catch (SOAPFault $e) {
    print_r('Exception:'.$e);
}
?>

在浏览器中执行后,返回

<?xml version="1.0" encoding="UTF-8"?>
<PACKAGE>
    <STATUS>0</STATUS>
    <REASON>入单成功!</REASON>
    <ORDERSEQ>2014100905523549742</ORDERSEQ>
</PACKAGE>

最普遍的一种说法就是,Web
Service = SOAP + HTTP + WSDL。其中,SOAP Simple Object Access
Protocol)协议是web
service的主体,它通过HTTP或者SMTP等应用层协议进行通讯,自身使用XML文件来描述程序的函数方法和参数信息,从而完成不同主机的异构系统间的计算服务处理。这里的WSDL(Web
Services Description Language)web
服务描述语言也是一个XML文档,它通过HTTP向公众发布,公告客户端程序关于某个具体的
Web service服务的URL信息、方法的命名,参数,返回值等。
下面,我们先来熟悉一下SOAP协议,看看它是如何描述程序中的函数方法、参数及结果对象的。

WebService框架核心是基于简单对象访问协议(Simple Object Access
Protocol,SOAP)、Web 服务描述语言(Web Service Description
Language,WSDL)以及通用描述、发现和集成(Universal Description
Discovery and Integration,UDDI)。

 

SOAP协议简介

                     
图片 1

什么是SOAP
SOAP
指简单对象访问协议,它是一种基于XML的消息通讯格式,用于网络上,不同平台,不同语言的应用程序间的通讯。可自定义,易于扩展。一条
SOAP 消息就是一个普通的 XML 文档,包含下列元素:

Envelope 元素,标识XML 文档一条 SOAP 消息

Header 元素,包含头部信息的XML标签

Body 元素,包含所有的调用和响应的主体信息的标签

Fault 元素,错误信息标签。

各部分详细介绍

以上的元素都在
SOAP的命名空间
SOAP的语法规则

SOAP 消息必须用 XML 来编码

SOAP 消息必须使用 SOAP Envelope 命名空间

SOAP 消息必须使用 SOAP Encoding 命名空间

SOAP 消息不能包含 DTD 引用

SOAP 消息不能包含 XML 处理指令

soap:简单对象访问协议,是一个基于xml访问的协议。它包含有四部分:SOAP封装(envelop),封装定义了一个描述消息中的内容是什么,是谁发送的,谁应当接受并处理它以及如何处理它们的框架;SOAP编码规则(encoding
rules),用于表示应用程序需要使用的数据类型的实例; SOAP RPC表示(RPC
representation),表示远程过程调用和应答的协定;SOAP绑定(binding),使用底层协议交换信息。SOAP是以HTTP作为底层通信协议,以RPC作为一致性的调用途径,以XML做为数据传输格式。可以简单理解为SOAP=HTTP+RPC+XML

SOAP
消息的基本结构

wsdl:用来描述WEB服务,他将Web服务描述定义为一组服务访问点,客户端可以通过这些服务访问点对包含面向文档信息或面向过程调用的服务进行访问(类似远程过程调用)。WSDL首先对访问的操作和访问时使用的请求/响应消息进行抽象描述,然后将其绑定到具体的传输协议和消息格式上以最终定义具体部署的服务访问点。

Java代码

uddi: UDDI是一个分布式的互联网服务注册机制,它集描述(Universal
Description)、检索(Discovery)与集成(Integration)为一体,其核心是注册机制。UDDI实现了一组可公开访问的接
口,通过这些接口,网络服务可以向服务信息库注册其服务信息、服务需求者可以找到分散在世界各地的网络服务。

  1. <? xml version=”1.0″?>  

  2. <soap:Envelope  

  3. xmlns:soap=”” 

  4. soap:encodingStyle=”;  

  5. <soap:Header>  

  6.   …  

  7.   …  

  8. </soap:Header>  

  9. <soap:Body>  

  10.   …  

  11.   …  

  12.  
    <soap:Fault>  

  13.     …  

  14.     …  

  15.  
    </soap:Fault>  

  16. </soap:Body>  

  17. </soap:Envelope> 

SOAP
Envelope 元素
Envelope
元素是 SOAP 消息的根元素。它指明 XML 文档是一个SOAP 消息。它的属性
xmlns:soap的值必须是

encodingStyle 属性,语法:soap:encodingStyle=”URI”
encodingStyle
属性用于定义文档中使用的数据类型。此属性可出现在任何 SOAP
元素中,并会被应用到元素的内容及元素的所有子元素上。

Java代码
图片 2

  1. <? xml version=”1.0″?>  

  2. <soap:Envelope  

  3. xmlns:soap=”” 

  4. soap:encodingStyle=”;  

  5.   …  

  6.   Message
    information goes here  

  7.   …  

  8. </soap:Envelope> 

SOAP
Header 元素

  • actor 属性,语法soap:actor=”URI”

通过沿着消息路径经过不同的端点,SOAP
消息可从某个发送者传播到某个接收者。并非 SOAP 消息的所有部分均打算传送到
SOAP
消息的最终端点,不过,另一个方面,也许打算传送给消息路径上的一个或多个端点。SOAP
的 actor 属性可被用于将 Header 元素寻址到一个特定的端点。

  • mustUnderstand 属性 ,语法soap:mustUnderstand=”0|1″

SOAP
的 mustUnderstand
属性可用于标识标题项对于要对其进行处理的接收者来说是强制的还是可选的。假如您向
Header 元素的某个子元素添加了
“mustUnderstand=”1″,则要求处理此头部的接收者必须认可此元素。

[java] view
plaincopy

 

  1. <? xml version=”1.0″?> 

  2. <soap:Envelope 

  3. xmlns:soap=”” 

  4. soap:encodingStyle=”; 
  5. <soap:Header> 

  6. <m:Trans 

  7. xmlns:m=””  

  8. soap:mustUnderstand=”1″  
  9. soap:actor=”
    “  >234</m:Trans> 

  10. </soap:Header> 

  11. … 

  12. … 
  13. </soap:Envelope> 

SOAP
Body 元素
必需的
SOAP Body 元素可包含打算传送到消息最终端点的实际 SOAP
消息。Body元素中既可以包含SOAP定义的命名空间中的元素,如Fault,也可以是用户的应用程序自定义的元素。以下是一个用户定义的请求:

[java] view
plaincopy

 

  1. <? xml version=”1.0″?> 

  2. <soap:Envelope 

  3. xmlns:soap=”” 

  4. soap:encodingStyle=”; 
  5. <soap:Body> 

  6.   
    <m:GetPrice xmlns:m=”; 

  7.      
    <m:Item>Apples</m:Item> 
  8.   
    </m:GetPrice> 
  9. </soap:Body> 

  10. </soap:Envelope> 

上面的例子请求苹果的价格。请注意,上面的
m:GetPrice 和 Item 元素是应用程序专用的元素。它们并不是 SOAP
标准的一部分。而对应的 SOAP 响应应该类似这样:

Java代码

  1. <?xml version=”1.0″?>  

  2. <soap:Envelope  

  3. xmlns:soap=”” 

  4. soap:encodingStyle=”;  

  5. <soap:Body>  

  6.   
    <m:GetPriceResponse xmlns:m=”;  

  7.      
    <m:Price>1.90</m:Price>  

  8.   
    </m:GetPriceResponse>  

  9. </soap:Body>  

  10. </soap:Envelope> 

SOAP
Fault 元素
Fault
元素表示 SOAP的错误消息。它必须是 Body 元素的子元素,且在一条 SOAP
消息中,Fault 元素只能出现一次。Fault 元素拥有下列子元素:
图片 3
常用的SOAP
Fault Codes
图片 4

HTTP协议中的SOAP
实例
下面的例子中,一个
GetStockPrice 请求被发送到了服务器。此请求有一个 StockName
参数,而在响应中则会返回一个 Price
参数。此功能的命名空间被定义在此地址中: “”

  • SOAP 请求:(注意HTTP的Head属性)

Java代码

  1. POST /InStock HTTP/1.1 
  2. Host:
    www.jsoso.net  
  3. Content-Type:
    application/soap+xml; charset=utf-8 

  4. Content-Length:
    XXX  

  5.  
  6. <? xml
    version=”1.0″?>  

  7. <soap:Envelope  

  8. xmlns:soap=”” 

  9. soap:encodingStyle=”;  

  10.  
    <soap:Body xmlns:m=”;  

  11.    
    <m:GetStockPrice>  

  12.      
    <m:StockName>IBM</m:StockName>  
  13.    
    </m:GetStockPrice>  
  14.  
    </soap:Body>    
  15. </soap:Envelope> 
  • SOAP 响应:(注意HTTP的Head属性)

Java代码

  1. HTTP/1.1 200 OK  

  2. Content-Type:
    application/soap+xml; charset=utf-8 

  3. Content-Length:
    XXX  

  4.  
  5. <? xml
    version=”1.0″?>  

  6. <soap:Envelope  

  7. xmlns:soap=”” 

  8. soap:encodingStyle=”;  

  9.  
    <soap:Body xmlns:m=”;  

  10.    
    <m:GetStockPriceResponse>  

  11.      
    <m:Price>34.5</m:Price>  

  12.    
    </m:GetStockPriceResponse>  

  13.  
    </soap:Body>    
  14. </soap:Envelope> 

HTTP协议中的SOAP
RPC工作流程
图片 5

WSDL简介
介绍过了SOAP,让我们关注Web
Service中另外一个重要的组成WSDL。
WSDL的主要文档元素
图片 6
WSDL文档可以分为两部分。顶部分由抽象定义组成,而底部分则由具体描述组成。抽象部分以独立于平台和语言的方式定义SOAP消息,它们并不包含任何随机器或语言而变的元素。这就定义了一系列服务,截然不同的应用都可以实现。具体部分,如数据的序列化则归入底部分,因为它包含具体的定义。在上述的文档元素中,<types>、<message>、<portType>属于抽象定义层,<binding>、<service>属于具体定义层。所有的抽象可以是单独存在于别的文件中,也可以从主文档中导入。

WSDL文档的结构实例解析
下面我们将通过一个实际的WSDL文档例子来详细说明各标签的作用及关系。

Java代码

  1. <?xml version=”1.0″ encoding=”UTF-8″?>  

  2. <definitions  

  3. xmlns:soap=”” 

  4. xmlns:tns=”” 

  5. xmlns:xsd=”” 

  6. xmlns=”” 

  7. targetNamespace=”” 

  8. name=”Example”>  

  9.  

  10. <types>  

  11.  
    <xsd:schema>  

  12.  
    <xsd:import 

  13.   
    namespace=”” 

  14.   
    schemaLocation=”;  

  15.  
    </xsd:schema>  

  16. </types>  

  17.  

  18. <message
    name=”toSayHello”>  

  19.   <part
    name=”userName” type=”xsd:string”></part>  

  20. </message>  

  21. <message
    name=”toSayHelloResponse”>  

  22.   <part
    name=”returnWord” type=”xsd:string”></part>  

  23. </message>  

  24.  

  25. <message
    name=”sayHello”>  

  26.   <part
    name=”person” type=”tns:person”></part>  

  27.   <part
    name=”arg1″ type=”xsd:string”></part>  

  28. </message>  

  29. <message
    name=”sayHelloResponse”>  

  30.   <part
    name=”personList” type=”tns:personArray”></part>  

  31. </message>  

  32. <message
    name=”HelloException”>  

  33.   <part
    name=”fault” element=”tns:HelloException”></part>  

  34. </message>  

  35.  

  36. <portType
    name=”Example”>  

  37.  
    <operation name=”toSayHello”
    parameterOrder=”userName”>  

  38.     <input
    message=”tns:toSayHello”></input>  

  39.    
    <output message=”tns:toSayHelloResponse”></output>  

  40.  
    </operation>  

  41.  
    <operation name=”sayHello”
    parameterOrder=”person arg1″>  

  42.     <input
    message=”tns:sayHello”></input>  

  43.    
    <output message=”tns:sayHelloResponse”></output>  

  44.     <fault
    message=”tns:HelloException” name=”HelloException”></fault>  

  45.  
    </operation>  

  46. </portType>  

  47.  

  48. <binding
    name=”ExamplePortBinding” type=”tns:Example”>  

  49.  
    <soap:binding  

  50.    
    transport=””   

  51.    
    style=”rpc”></soap:binding>  

  52.  
    <operation name=”toSayHello”>  

  53.    
    <soap:operation soapAction=”sayHello”></soap:operation>  

  54.    
    <input>  

  55.      
    <soap:body use=”literal” 

  56.        
    namespace=”;  

  57.    
    </input>  

  58.    
    <output>  
  59.      
    <soap:body use=”literal” 

  60.         
    namespace=”;  

  61.    
    </output>  

  62.  
    </operation>  
  63.  
    <operation name=”sayHello”>  

  64.    
    <soap:operation soapAction=”sayHello”></soap:operation>  

  65.    
    <input>  

  66.      
    <soap:body use=”literal” 

  67.        
    namespace=”;  

  68.    
    </input>  

  69.    
    <output>  
  70.      
    <soap:body use=”literal” 

  71.        
    namespace=”;  

  72.    
    </output>  

  73.     <fault
    name=”HelloException”>  

  74.      
    <soap:fault name=”HelloException” use=”literal”></soap:fault>  

  75.    
    </fault>  

  76.    
    </operation>  
  77. </binding>  

  78.  

  79. <service
    name=”Example”>  

  80.   <port
    name=”ExamplePort” binding=”tns:ExamplePortBinding”>  

  81.    
    <soap:address location=”;  

  82.  
    </port>  

  83. </service>  

  84. </definitions> 

由于上面的事例XML较长,我们将其逐段分解讲解

WSDL文档的根元素:<definitions>

Java代码

  1. <definitions  
  2. xmlns:soap=”” 

  3. xmlns:tns=”” 

  4. xmlns:xsd=”” 

  5. xmlns=”” 

  6. targetNamespace=”” 

  7. name=”Example”>  

  8. ……  

  9. ……  
  10. </definitions> 

<definitions>定义了文档中用到的各个xml元素的namespace缩写,也界定了本文档自己的targetNamespace=”

引用

WSDL文档数据类型定义元素:<types>

Java代码

  1. <types>  
  2.  
    <xsd:schema>  
  3.  
    <xsd:import 

  4.   
    namespace=”” 

  5.   
    schemaLocation=”;  

  6.  
    </xsd:schema>  

  7. </types> 

<types>标签定义了当前的WSDL文档用到的数据类型。要说明的是,为了最大程度的平台中立性,WSDL
使用 XML Schema 语法来定义数据类型。这些数据类型用来定义web
service方法的参数和返回指。对于通用的原生数据类型如:integer , boolean ,
char ,
float等,在W3C的标准文档

WSDL文档消息体定义元素:<
message >

Java代码

  1. <message name=”toSayHello”>  

  2.   <part
    name=”userName” type=”xsd:string”></part>  

  3. </message>  

  4. <message
    name=”toSayHelloResponse”>  

  5.   <part
    name=”returnWord” type=”xsd:string”></part>  

  6. </message>  

  7.  

  8. <message
    name=”sayHello”>  

  9.   <part
    name=”person” type=”tns:person”></part>  

  10.   <part
    name=”arg1″ type=”xsd:string”></part>  

  11. </message>  

  12. <message
    name=”sayHelloResponse”>  

  13.   <part
    name=”personList” type=”tns:personArray”></part>  

  14. </message>  

  15. <message
    name=”HelloException”>  

  16.   <part
    name=”fault” element=”tns:HelloException”></part>  

  17. </message> 

<message>元素定义了web
service函数的参数。<message>元素中的每个<part>子元素都和某个参数相符。输入参数在<message>元素中定义,与输出参数相隔离,输出参数有自己的<message>元素。兼作输入、输出的参数在输入输出的<message>元素中有它们相应的<part>元素。输出<message>元素以”Response”结尾,对Java而言方法得返回值就对应一个输出的<message>。每个<part>元素都有名字和类型属性,就像函数的参数有参数名和参数类型。

在上面的文档中有两个输入参数、两个输出参数和一个错误参数(对应Java中的Exception)。


输入参数<message>的name属性分别命名为toSayHello,sayHello。
toSayHello对应输入参数userName,参数类型为xsd:string,在Java语言中就是String;
sayHello对应两个输入参数person和arg1,类型为tns:person和xsd:string。这里tns:person类型就是引用了<
types >标签中的类型定义。


输出参数<message>的name属性分别命名为toSayHelloResponse和sayHelloResponse。
这个名称和输入参数的<message>标签name属性对应,在其后面加上Response尾缀。
toSayHelloResponse对应的返回值是returnWord,参数类型为xsd:string;
sayHelloResponse对应的返回值是personList,参数类型为tns:personArray(自定义类型);


错误参数<message>的name属性为HelloException。
它的<part>子标签element而不是type来定义类型。

以上的message标签的name属性通常使用web
service函数方法名作为参照,错误参数标签则使用异常类名为参照。标签中的参数名称,即part子元素的name属性是可自定义的(下一章节详细说明)。message标签的参数类型将引用types标签的定义。

WSDL文档函数体定义元素:<
portType
>

[java] view
plaincopy

 

  1. <portType name=”Example”> 

  2.  
    <operation name=”toSayHello” parameterOrder=”userName”> 

  3.     <input
    message=”tns:toSayHello”></input> 

  4.    
    <output message=”tns:toSayHelloResponse”></output> 

  5.  
    </operation> 

  6.  
    <operation name=”sayHello” parameterOrder=”person arg1″> 

  7.     <input
    message=”tns:sayHello”></input> 

  8.    
    <output message=”tns:sayHelloResponse”></output> 

  9.     <fault
    message=”tns:HelloException” name=”HelloException”></fault> 

  10.  
    </operation> 

  11. </portType> 

<portType>
元素是最重要的 WSDL 元素。它可描述一个 web
service、可被执行的操作,以及相关的消息。portType的name属性对应Java中的一个服务类的类名。<portType>
元素使用其子元素< operation>描述一个web service的服务方法。

在<operation>元素中,name属性表示服务方法名,parameterOrder属性表示方法的参数顺序,使用空格符分割多个参数,如:“parameterOrder=”person
arg1”。<operation>元素的子标签<input>表示输入参数说明,它引用<message>标签中的输入参数。<output>表示输出参数说明,它引用<message>标签中的输出参数。<fault>标签在Java方法中的特别用来表示异常(其它语言有对应的错误处理机制),它引用<message>标签中的错误参数。

WSDL绑定实现定义元素:<
binding
>

[java] view
plaincopy

 

  1. <binding name=”ExamplePortBinding” type=”tns:Example”> 

  2.  
    <soap:binding 

  3.    
    transport=””  
  4.    
    style=”rpc”></soap:binding> 

  5.  
    <operation name=”toSayHello”> 

  6.    
    <soap:operation soapAction=”sayHello”></soap:operation> 

  7.    
    <input> 

  8.      
    <soap:body use=”literal” 
  9.        
    namespace=”; 

  10.    
    </input> 

  11.    
    <output> 
  12.      
    <soap:body use=”literal” 
  13.         
    namespace=”; 

  14.    
    </output> 

  15.  
    </operation> 
  16.  
    <operation name=”sayHello”> 
  17.    
    <soap:operation soapAction=”sayHello”></soap:operation> 

  18.    
    <input> 

  19.      
    <soap:body use=”literal” 
  20.        
    namespace=”; 

  21.    
    </input> 

  22.    
    <output> 
  23.      
    <soap:body use=”literal” 
  24.        
    namespace=”; 

  25.    
    </output> 

  26.     <fault
    name=”HelloException”> 
  27.      
    <soap:fault name=”HelloException” use=”literal”></soap:fault> 

  28.    
    </fault> 

  29.    
    </operation> 
  30. </binding> 

<binding>标签是完整描述协议、序列化和编码的地方,<types>,<message>和<portType>标签处理抽象的数据内容,而<binding>标签是处理数据传输的物理实现。
<binding>标签把前三部分的抽象定义具体化。

首先<binding>标签使用<soap:binding>的transport和style属性定义了Web
Service的通讯协议HTTP和SOAP的请求风格RPC。其次<operation>子标签将portType中定义的operation同SOAP的请求绑定,定义了操作名称soapAction,输出输入参数和异常的编码方式及命名空间。

WSDL服务地址绑定元素:<
service
>

[java] view
plaincopy

 

  1. <service name=”Example”> 

  2.   <port
    name=”ExamplePort” binding=”tns:ExamplePortBinding”> 

  3.    
    <soap:address location=”; 

  4.  
    </port> 

  5. </service> 

service是一套<port>元素。在一一对应形式下,每个<port>元素都和一个location关联。如果同一个<binding>有多个<port>元素与之关联,可以使用额外的URL地址作为替换。

一个WSDL文档中可以有多个<service>元素,而且多个<service>元素十分有用,其中之一就是可以根据目标URL来组织端口。在一个WSDL文档中,<service>的name属性用来区分不同的service。在同一个service中,不同端口,使用端口的”name”属性区分。

这一章节,我们简单的描述了WSDL对SOAP协议的支持,以及在Web
Service中的作用。在接下来的章节中,我们将学习如何使用Java6.0的Annotation标签来定义和生成对应的WSDL。

JavaSE6.0下的Web
Service

从JavaSE6.0开始,Java引入了对Web
Service的原生支持。我们只需要简单的使用Java的Annotation标签即可将标准的Java方法发布成Web
Service。(PS:Java Annotation资料请参考  JDK5.0
Annotation学习笔记(一)

但不是所有的Java类都可以发布成Web
Service。Java类若要成为一个实现了Web
Service的bean,它需要遵循下边这些原则:

  •  这个类必须是public类
  •  这些类不能是final的或者abstract
  •  这个类必须有一个公共的默认构造函数
  •  这个类绝对不能有finalize()方法

下面我们将通过一个具体的Java
Web Service代码例子,配合上述的WSDL文件,讲述如何编写JavaSE6.0的原生Web
Service应用。

完整的Java
Web Service类代码

 

[java] view
plaincopy

 

  1. package org.jsoso.jws.server; 

  2.  

  3. import java.util.ArrayList; 

  4. import javax.jws.WebMethod; 

  5. import javax.jws.WebParam; 

  6. import javax.jws.WebResult; 

  7. import javax.jws.WebService; 

  8. import javax.jws.WebParam.Mode; 

  9. import javax.jws.soap.SOAPBinding; 

  10. *
    提供WebService服务的类 
  11. */ 
  12. @WebService(name=”Example”, targetNamespace=””, serviceName=”Example”) 

  13. @SOAPBinding(style=SOAPBinding.Style.RPC) 

  14. public class Example { 

  15.     private ArrayList<Person> persons
    = new ArrayList<Person>();; 

  16.     /**

  17.      *

  18.      *
    返回一个字符串

  19.      * @param
    userName
  20.      * @return

  21.      */ 

  22.     @WebMethod(operationName=”toSayHello”,action=”sayHello”,exclude=false) 

  23.     @WebResult(name=”returnWord”)//自定义该方法返回值在WSDL中相关的描述 

  24.     public String sayHello(@WebParam(name=”userName”)String userName) { 

  25.         return “Hello:” + userName; 

  26.     } 

  27.  

  28.     /**

  29.      * web
    services 方法的返回值与参数的类型不能为接口

  30.      * @param
    person
  31.      * @return

  32.      * @throws
    HelloException

  33.      */ 
  34.     @WebMethod(operationName=”sayHello”, action=”sayHello”) 

  35.     @WebResult(partName=”personList”) 

  36.     public Person[] sayHello(@WebParam(partName=”person”, mode=Mode.IN)Person person,  

  37.            
    String userName) throws HelloException { 

  38.         if (person == null || person.getName() == null) { 

  39.            
    throw new HelloException(“说hello出错,对像为空。。”); 

  40.         } 

  41.        
    System.out.println(person.getName() + ” 对 ” + userName + ”
    说:Hello,我今年” +
    person.getAge() + “岁”); 

  42.        
    persons.add(person); 

  43.         return persons.toArray(new Person[0]); 

  44.     } 

Annotation
1@WebService(name=”Example”,
targetNamespace=””,
serviceName=”Example”)
@WebService标签主要将类暴露为WebService,其中targetNamespace属性定义了自己的命名空间,serviceName则定义了<
definitions >标签和<service>标签的name属性。

Annotation
2:@SOAPBinding(style=SOAPBinding.Style.RPC)
@SOAPBinding标签定义了WSDL文档中SOAP的消息协议,其中style属性对应SOAP的文档类型,可选的有RPC和DOCUMENT

Annotation
3:@WebMethod(operationName=”toSayHello”,action=”sayHello”,exclude=false)
@WebMethod定义Web
Service运作的方法,
属性action
对应操作的活动 ,如<soap:operation soapAction=”sayHello” />
属性operationName匹配的wsdl:operation
的名称,如<operation name=”toSayHello”
parameterOrder=”userName”>
属性exclude
用于阻止将某一继承方法公开为web服务,默认为false

Annotation
4:@WebResult(name=”returnWord”)
@
WebResult定义方法返回值得名称,如<part name=”returnWord”
type=”xsd:string” />

Annotation
5:@WebParam(partName=”person”, mode=Mode.IN
@WebParam定义方法的参数名称,如<part
name=”person” type=”tns:person”
/>,其中mode属性表示参数的流向,可选值有IN / OUT / INOUT

这里要着重说明的是,上述Web
Service类的sayHello方法中,带有HelloException这个异常声明,造成该服务类不能直接发布成Web
Service。需要使用wsgen工具为其生存异常Bean。关于wsgen工具的使用,请参考wsgen与wsimport命令说明

发布一个的Java
Web Service
在完成了上述的Web
Service
Annotation注释后,我们使用wsgen工具为其进行服务资源文件的构造(这里主要是生成一个名为org.jsoso.jws.server.jaxws.HelloExceptionBean的异常bean类),最后使用以下的类发布Web
服务:

[java] view
plaincopy

 

  1. package org.jsoso.jws.server; 

  2.  

  3. import java.util.LinkedList; 

  4. import java.util.List; 

  5. import javax.xml.ws.Binding; 

  6. import javax.xml.ws.Endpoint; 

  7. import javax.xml.ws.handler.Handler; 

  8.  

  9. /**

  10. * @author zsy
    启动web services服务

  11. */ 
  12. public class StartServer { 

  13.  

  14.     /**

  15.      * @param
    args

  16.      */ 
  17.     public static void main(String[] args) { 

  18.         /*

  19.          *
    生成Example 服务实例

  20.          */ 
  21.        
    Example serverBean = new Example(); 

  22.         /*

  23.          *
    发布Web Service到

  24.          */ 
  25.        
    Endpoint endpoint =  
  26.           
    Endpoint.publish(“”, serverBean); 

  27.        
    Binding binding = endpoint.getBinding(); 

  28.         /*

  29.          *
    设置一个SOAP协议处理栈

  30.          *
    这里就简单得打印SOAP的消息文本
  31.          */ 
  32.        
    List<Handler> handlerChain = new LinkedList<Handler>(); 

  33.        
    handlerChain.add(new TraceHandler()); 

  34.        
    binding.setHandlerChain(handlerChain); 

  35.        
    System.out.println(“服务已启动
    “); 
  36.     } 

在控制台运行这个类,就可以使用URL

浏览到上文所描述的WSDL的全文了。这说明您的第一个Web
Service应用发布成功!

构建Web
Service客户端
使用JavaSE6.0构建Web
Service的客户端是一件相当简单的事。这里我们要使用到JDK中的另一个命令行工具wsimport。在控制台下输入以下命令:

引用

wsimport -d ./bin -s ./src -p org.jsoso.jws.client.ref

即可在包org.jsoso.jws.client.ref中生成客户端的存根及框架文件。其中我们要使用的类只有两个:服务类Example_Service和本地接口Example。编写如下客户端,即可调用Web
Service服务:

[java] view
plaincopy

 

  1. package org.jsoso.jws.client; 

  2.  

  3. import org.jsoso.jws.client.ref.*; 

  4.  

  5. public class RunClient { 

  6.  

  7.     /**

  8.      * @param
    args

  9.      */ 
  10.     public static void main(String[] args) { 

  11.        
    //初始化服务框架类 

  12.        
    Example_Service service = new Example_Service(); 

  13.        
    //或者本地服务借口的实例 

  14.        
    Example server = (Example) service.getExamplePort(); 

  15.         try { 
  16.            
    //调用web
    service的toSayHello方法 

  17.            
    System.out.println(“输入toSayHello的返回值——” + server.toSayHello(“阿土”));          

  18.             
    Person person = new Person(); 

  19.             
    person.setName(“阿土”); 
  20.             
    person.setAge(25); 
  21.             
    //调用web
    service的sayHello方法 

  22.             
    server.sayHello(person, “机器人”); 

  23.               

  24.             
    person = new Person(); 

  25.             
    person.setName(“aten”); 
  26.             
    person.setAge(30); 
  27.             
    //调用web
    service的sayHello方法 

  28.             
    PersonArray list = server.sayHello(person, “机器人”); 

  29.            

    //输出返回值 

  30.             
    System.out.println(“/n以下输入sayHello的返回值——”); 

  31.            
    for (Person p : list.getItem()) { 

  32.                
    System.out.println(p.getName() + “:” + p.getAge()); 

  33.            
    }            

  34.         }
    catch (HelloException_Exception e) { 

  35.            
    e.printStackTrace(); 

  36.         } 

  37.     } 

届此,本次Web
Service的学习暂告一个段落。Java Web
Service是一个相当庞大的知识体系,其中涉及的相关技术较多,这里无法一一道来,我们将会在今后的开发和使用中,同大家做进一步深入的探讨和学习。

附录:wsgen与wsimport命令说明

wsgen
wsgen是在JDK的bin目录下的一个exe文件(Windows版),该命令的主要功能是用来生成合适的JAX-WS。它读取Web
Service的终端类文件,同时生成所有用于发布Web
Service所依赖的源代码文件和经过编译过的二进制类文件。这里要特别说明的是,通常在Web
Service Bean中用到的异常类会另外生成一个描述Bean,如果Web Service
Bean中的方法有申明抛出异常,这一步是必需的,否则服务器无法绑定该对像。此外,wsgen还能辅助生成WSDL和相关的xsd文件。wsgen从资源文件生成一个完整的操作列表并验证web
service是否合法,可以完整发布。
命令参数说明:

  •  -cp 定义classpath
  •  -r 生成 bean的wsdl文件的存放目录
  •  -s 生成发布Web
    Service的源代码文件的存放目录(如果方法有抛出异常,则会生成该异常的描述类源文件)
  •  -d 生成发布Web
    Service的编译过的二进制类文件的存放目录(该异常的描述类的class文件)

命令范例:wsgen
-cp ./bin -r ./wsdl -s ./src -d ./bin -wsdl
org.jsoso.jws.server.Example

wsimport
wsimport也是在JDK的bin目录下的一个exe文件(Windows版),主要功能是根据服务端发布的wsdl文件生成客户端存根及框架,负责与Web
Service
服务器通信,并在将其封装成实例,客户端可以直接使用,就像使用本地实例一样。对Java而言,wsimport帮助程序员生存调用web
service所需要的客户端类文件.java和.class。要提醒指出的是,wsimport可以用于非Java的服务器端,如:服务器端也许是C#编写的web
service,通过wsimport则生成Java的客户端实现。
命令参数说明:

  •  -d 生成客户端执行类的class文件的存放目录
  •  -s 生成客户端执行类的源文件的存放目录
  •  -p 定义生成类的包名

命令范例:wsimport
-d ./bin -s ./src -p org.jsoso.jws.client.ref

发表评论

电子邮件地址不会被公开。 必填项已用*标注