開發 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 元素順序一致。
