开发 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 文件
<?xml version="1.0" encoding="UTF-8"?>
第二步:定义 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 元素
<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 元素
-
<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 元素有一个或多个 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 元素
-
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 元素
<wsdl:service name="StudentService"> <wsdl:port name="StudentServicePort" binding="tns:StudentServiceBinding"> <soap:address location="http://example.com/student/index.php?wsdl" /> </wsdl:port> </wsdl:service>
编写结束后将文件保存为 student.wsdl。
开发服务端
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'; }
客户端调用
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()); }
<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>
$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); }
<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]);
$std = new stdClass(); $std->id = [1, 2, 3]; $backlog = $soap->add($std);
此时服务端接收到的数据格式与之前的相同。
客户端调用:
$backlog = $soap->add([1]);
服务端接口数据为:["id":"1"]。
示例三:传入多维数组
[ "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>
客户端调用:
$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 元素顺序一致。