开发 WebService 服务首先需要根据接口的要求编写相关的 wsdl 文件。编写 wsdl 文件需要先对 XML 语法、XML Schema 语法以及 SOAP 语法有一些简单了解。
假设需要提供一个 Student 服务,该服务仅提供一个操作:add($student),通过 add 方法添加学生信息。即需要定义一个 Student 类,该类仅包括一个方法 add,该方法的参数是一个一维数组,数组键名包括:name(string)/sex(int)/age(int)。现在开始定义对应的 wsdl 文件。
wsdl 文件基本结构
wsdl 文件的结构大致如下:
1 <deFinitions> 2 <types> 3 deFinition of types........ 4 </types> 5 6 <message> 7 deFinition of a message.... 8 </message> 9 10 <portType> 11 <operation> 12 deFinition of a operation....... 13 </operation> 14 </portType> 15 16 <binding> 17 deFinition of a binding.... 18 </binding> 19 20 <service> 21 deFinition of a service.... 22 </service> 23 </deFinitions>
主要有五个元素构成:types、message、portType、binding、service。根元素是 deFinitions。
第一步:声明 wsdl 文件
wsdl 文件本质仍是一个 XML 文件,所以第一行进行 XML 文件声明:<?xml version="1.0" encoding="UTF-8"?>
第二步:定义 deFinitions 根元素
定义 deFinitions 元素时,需要定义之后将要使用的命名空间及其他属性。<wsdl:deFinitions name="StudentService" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://example.com/student/index.PHP" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://example.com/student/index.PHP" > ... </wsdl:deFinitions>
xmlns:soap、xmlns:wsdl、xmlns:xsd 这三个命名空间是默认的命名空间,其他默认的命名空间还有:
- targetNamesapce:指定该服务所属的命名空间,作用相当于 Java 中的 package。防止与其他 wsdl 文件冲突。一般指定为服务地址即可。
- xmlns:soap:定义 soap 命名空间,指定该服务遵守的 soap 版本。
- xmlns:wsdl:定义 wsdl 命名空间。该服务遵守的 wsdl 规范。注意:如果不定义该命名空间,则需要定义一个默认命名空间,即xmlns="http://schemas.xmlsoap.org/wsdl/"。定义该命名空间后,所有的 wsdl 元素都需要加载 wsdl 前缀。
- xmlns:xsd:定义 xsd 命名空间。XML Schema 实例遵守的规范。
- xmlns:tns:自定义命名空间。tns 即 targetNamespace,该文件中定义的新元素属于该命名空间。一般定义为服务地址即可。
第三步:定义 types 元素
当服务需要的数据类型超出 XML 内置的数据类型范围时,需要在 types 元素内通过 XML Schema 定义新的数据类型。 根据 Student 服务 add 方法的需求,需要定义 addRequest 数据类型表示请求参数的结构,定义 addResponse 数据类型表示响应消息的结构。 add 方法接受一个一维数组作为参数,键名为 name、sex、age。返回一个一位数组,键名 msg。 所以 types 元素的结构为:<wsdl:types> <!--xsd:schema元素的targetNamespace属性必需,且值与deFinitions元素的xmlns:tns相同--> <xsd:schema
targetNamespace="http://example.com/student/index.PHP"> <!--定义addRequest数据类型,属于tns命名空间--> <xsd:element name="addRequest"> <xsd:complexType> <xsd:sequence> <xsd:element name="name" type="xsd:string" minOccurs="0" /> <xsd:element name="sex" type="xsd:string" minOccurs="0" /> <xsd:element name="age" type="xsd:string" minOccurs="0" /> </xsd:sequence> </xsd:complexType> </xsd:element> <!--定义addResponse数据类型,属于tns命名空间--> <xsd:element name="addResponse"> <xsd:complexType> <xsd:sequence> <xsd:element name="msg" type="xsd:string" minOccurs="1" /> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:schema> </wsdl:types>
<xsd:schema>元素中必须指定 targetNamespace 属性,值与deFinitions元素的xmlns:tns属性值相同。表示定义的新元素的隶属命名空间。
第四步:定义 message 元素
每一个 WebService 服务都有都需要定义两个 message 元素:input 和 output。input 用于描述 WebService 服务接收的参数,output 描述 WebService 服务响应消息。 每一个 message 元素由 0 或多个 part 元素构成。一个 part 元素表示一个请求或响应参数。 part 元素有两种写法:- <part name="fristParam" type="xsd:string" />:描述基本类型的参数。
- <part name="secondParam" element="tns:addRequest" />:描述复杂类型的参数。
<!--input message:addRequest message,隶属命名空间tns,数据类型是XML Schema中定义的addRequest--> <wsdl:message name="addRequest"> <wsdl:part element="tns:addRequest" name="request" /> </wsdl:message> <!--output message:addResponse message,隶属命名空间tns,数据类型是XML Schema中定义的addResponse--> <wsdl:message name="addResponse"> <wsdl:part element="tns:addResponse" name="response" /> </wsdl:message>
第五步:定义 portType 元素
portType 元素描述了一个完整的 WebService 服务操作。通过 portType 元素定义不同的 port,用于 binding 元素的绑定操作。这些类型的 port 属于 tns 命名空间。- 一个 portType 元素有一个或多个 operation 元素构成。
- 一个 operation 元素表示一个 WebService 服务,通过 operation 元素的 name 属性定义对应的 WebService 服务操作。
- 一个 operation 元素通常包括一个 input 元素 和一个 output 元素。
<!--name属性定义portType--> <wsdl:portType name="StudentService"> <!--该operation元素对应add服务--> <wsdl:operation name="add"> <!--input的消息类型是tns:addRequest--> <wsdl:input name="addInput" message="tns:addRequest" /> <!--output的消息类型是tns:addResponse--> <wsdl:output name="addOutput" message="tns:addResponse" /> </wsdl:operation> </wsdl:portType>
第六步:定义 binding 元素
binding 元素描述了 WebService 服务如何绑定到消息协议,包括指定数据传输方式(HTTP GET/HTTP POST/SOAP),指定传输协议,指定服务地址等。 binding 元素有两个属性:name 和 type。name 属性定义 binding 元素名称,type 属性定义该 binding 指向哪一个类型的 port,即之前定义 portType 元素。type 值即 portType 元素的 name 值。 WSDL1.1 中引入了 soap 的扩展元素:soap:binding/soap:operation/soap:body。binding 元素主要由这三个元素构成。- soap:binding:包括 style 属性和 transport 属性。style 属性定义了 soap 消息格式的整体样式,值为 rpc 或 document。这两个值仅表示两种不同的将 WSDL binding 转换为 SOAP message 的方式,选择任一种即可。transport 属性指定传输协议,值为http://schemas.xmlsoap.org/soap/http,表示通过soap HTTP 方式传输;值为http://schemas.xmlsoap.org/soap/smtp,表示通过 SOAP SMTP方式传输。
- soap:operation:将指定的 WebService 服务绑定到指定的 SOAP 实现。属性 soapAction 指定 soapAction HTTP header 内容以标识对应的服务。
- soap:body:包括 use 属性,值为 encoded 和 literal。指定 input 和 output 的 message 细节。
注意:这四种组合的具体差别参考:https://www.ibm.com/developerworks/library/ws-whichwsdl/
根据服务示例定义 binding 元素:
<wsdl:binding name="StudentServiceBinding" type="tns:StudentService"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="add"> <soap:operation soapAction="http://example.com/student/index.PHP#add" /> <wsdl:input name="addInput"> <soap:body use="literal" /> </wsdl:input> <wsdl:output name="addOutput"> <soap:body use="literal" /> </wsdl:output> </wsdl:operation> </wsdl:binding>
第七步:定义 service 元素
service 元素定义 WebService 支持的 port。每一个支持的协议都有一个对应的 port 元素。service 元素就是一系列 port 元素的集合。 service 元素内的 port 元素的 binding 属性关联 binding 元素的 name 属性。 service 元素内的 port 元素内的 soap:address 元素的 location 属性是 WebService 服务的地址。 根据服务示例定义 service 元素:<wsdl:service name="StudentService"> <wsdl:port name="StudentServicePort" binding="tns:StudentServiceBinding"> <soap:address location="http://example.com/student/index.PHP" /> </wsdl:port> </wsdl:service>
编写结束后将文件保存为 student.wsdl。
开发服务端
addRequest message 中仅有一个 part 元素,表示 add 方法只有一个参数。定义 Student 类: Student.PHP:class Student { public function add($student) { return [ 'msg'=>json_encode($studentList, JSON_UnesCAPED_UNICODE)]; } }
定义 index.PHP:
require './Student.PHP'; if(isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'POST') { try { $wsdl = './student.wsdl'; $soap = new SoapServer($wsdl); $soap->setClass('Student'); $soap->handle(); }catch (SoapFault $fault){ echo $fault->getMessage(); } }elseif(isset($_SERVER['REQUEST_METHOD']) && strtolower($_SERVER['QUERY_STRING']) == 'wsdl' && $_SERVER['REQUEST_METHOD'] == 'GET') { header('Content-type: text/xml; charset=utf-8'); $wsdl = file_get_contents('./student.wsdl'); echo $wsdl; }else { echo 'No wsdl xml file'; }
客户端调用
client.PHP:try { $soap = new SoapClient('http://127.0.0.1:8091/exercise/soap/index.PHP?wsdl', ['trace'=>true, 'cache_wsdl'=>WSDL_CACHE_NONE]); $studentInfo = ['name' => 'a', 'sex'=>'0', 'age'=>'20']; $backlog = $soap->add($studentInfo); var_dump($backlog); }catch(Exception $e) { var_dump($e->getMessage()); }
不同类型传参时的 message 元素定义
示例一:请求参数和响应数据都是基本类型
public function($int1, $str1) { return json_encode(func_get_args()); }请求参数有两个,一个是 int,一个是 string。响应消息是一个 string。 message 元素定义:
<wsdl:message name="addRequest"> <wsdl:part name="int1" type="xsd:int" /> <wsdl:part name="str1" type="xsd:string" /> </wsdl:message> <wsdl:message name="addResponse"> <wsdl:part name="return" type="xsd:string" /> </wsdl:message>此时不需要再定义 types 元素。因为请求和响应的数据都是基本类型,不需要定义额外的数据类型。 客户端调用:
$soap = new SoapClient('http://192.168.12.118:8091/exercise/soap/index.PHP?wsdl', ['trace'=>true, 'cache_wsdl'=>WSDL_CACHE_NONE]); $backlog = $soap->add(20, 'age'); var_dump($backlog);
示例二:请求参数是长度不定的一维数组
public function($idList) { return json_encode($idList); }因为数组已经不是 XML 内置的基本数据类型了,因此需要在 types 元素内通过 XML Schema 定义一个数组的数据类型。 types 元素定义:
<wsdl:types> <xsd:schema targetNamespace="http://example.com/student/index.PHP"> <!--定义addRequest数据类型,属于tns命名空间--> <xsd:element name="addRequest"> <xsd:complexType> <xsd:sequence> <!--maxOccurs="unbounded"属性表示该元素出现的最大次数不定,通过该属性定义一个长度不定的数组--> <xsd:element name="id" type="xsd:string" minOccurs="0" maxOccurs="unbounded" /> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:schema> </wsdl:types>
message 元素定义:
<wsdl:message name="addRequest"> <wsdl:part name="idList" element="tns:addRequest" /> </wsdl:message> <wsdl:message name="addResponse"> <wsdl:part name="return" type="xsd:string" /> </wsdl:message>
客户端调用:
$soap = new SoapClient('http://192.168.12.118:8091/exercise/soap/index.PHP?wsdl', ['trace'=>true, 'cache_wsdl'=>WSDL_CACHE_NONE]); $backlog = $soap->add([1,2,3]);此时服务端接收的数据结构是:["id":["1","2","3"]],一个二维数组,二维数组包含一个键名为“id”的元素,该元素就是客户端传过来的一维数组。 也可以以对象类型调用:
$std = new stdClass(); $std->id = [1, 2, 3]; $backlog = $soap->add($std);
此时服务端接收到的数据格式与之前的相同。
注意:当客户端的请求参数是只有一个元素的一维数组时,服务端接收的数据不再是一个二维数组,也是一个一维数组。客户端调用:
$backlog = $soap->add([1]);
服务端接口数据为:["id":"1"]。
示例三:传入多维数组
现在设定 add 方法接受一个参数,该参数是一个多为数组,结构如下:[ "num": "传入的学生信息数量", "studentList": [ ["name":1, "age":20, "sex": 0], ["name":2, "age":20, "sex": 0], ... ] ]
服务端方法:
public function($studentInfo) { return json_encode($studentInfo); }
types 元素和 message 元素的定义:
<wsdl:types> <xsd:schema targetNamespace="http://example.com/student/index.PHP"> <xsd:element name="addRequest"> <xsd:complexType> <xsd:sequence> <xsd:element name="num" type="xsd:int" /> <xsd:element name="studentList" type="tns:studentList" /> </xsd:sequence> </xsd:complexType> </xsd:element> <!--定义studentList数据结构,长度不定的数组--> <xsd:complexType name="studentList"> <xsd:sequence> <xsd:element name="student" type="tns:student" minOccurs="0" maxOccurs="unbounded" /> </xsd:sequence> </xsd:complexType> <!--定义student数据结构--> <xsd:complexType name="student"> <xsd:sequence> <xsd:element name="name" type="xsd:string" minOccurs="0" /> <xsd:element name="age" type="xsd:string" minOccurs="0" /> <xsd:element name="sex" type="xsd:string" minOccurs="0" /> </xsd:sequence> </xsd:complexType> </xsd:schema> </wsdl:types> <wsdl:message name="addRequest"> <wsdl:part name="studentInfo" element="tns:addRequest" /> </wsdl:message> <wsdl:message name="addResponse"> <wsdl:part name="return" type="xsd:string" /> </wsdl:message>定 add 方法接受一个参数,该参数是一个多为数组,结构如下:
客户端调用:
$soap = new SoapClient('http://192.168.12.118:8091/exercise/soap/index.PHP?wsdl', ['trace'=>true, 'cache_wsdl'=>WSDL_CACHE_NONE]); $studentInfo = ['name' => 'a', 'sex'=>'0', 'age'=>'20']; $list = [ 'num' => 2, 'studentList' => [$studentInfo, $studentInfo], ]; $backlog = $soap->add($list);
注意:传入请求参数时,务必保持参数的传入顺序和 input message 中的 part 元素顺序一致。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。