對 SOAP 的相關學習就先告此一段落,這是最後一篇文章用來記錄下學習過程當中的一些筆記和心得。php
前面三篇文章分別是:git
《SOAP 介紹》github
《PHP SOAP 擴展的使用》segmentfault
由於 SOAP Web 服務是基於 HTTP 協議的,發出一個 SOAP 消息請求,實際上利用 HTTP 動詞中的 POST
,而後把 SOAP 消息放置在 HTTP body 裏面發送。簡單來講就是:每調用一次 SOAP 服務,就是發送一條 POST 請求。數組
下面是一次請求接口所發送的內容:服務器
POST /webservices/qqOnlineWebService.asmx HTTP/1.1 Host: www.webxml.com.cn Connection: Keep-Alive User-Agent: PHP-SOAP/5.4.29 Content-Type: application/soap+xml; charset=utf-8; action="http://WebXml.com.cn/qqCheckOnline" Content-Length: 247 <?xml version="1.0" encoding="UTF-8"?> <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://WebXml.com.cn/"> <env:Body> <ns1:qqCheckOnline> <ns1:qqCode>8698053</ns1:qqCode> </ns1:qqCheckOnline> </env:Body> </env:Envelope>
咱們能夠看到,一次 SOAP 請求,實際上就是向服務器端發送了一個 POST
請求。而發送的內容正是 SOAP 消息(它代表你本次調用的接口方法以及參數等等)。網絡
這個 POST 請求,有一些特色,好比:它發送的內容類型爲:application/soap+xml
,用戶代理爲 PHP-SOAP/5.4.29
,其餘特色本身觀察,很少說。app
實際上,既然咱們知道請求一次接口只是發送一次 POST 請求,那麼咱們徹底可使用一些工具或 PHP 自己自帶的一些庫(好比 curl、fsockopen)模擬發送 POST 請求,而不須要使用 PHP 的 SOAP 擴展。對,沒錯!在 PHP 尚未提供 SOAP 擴展前,的確不少人也是這樣作的。curl
那幹嗎要用 SOAP 擴展呢? 由於它官網的唄,由於它用 C 語言寫的,速度槓槓的,並且封裝得很好用,也不須要本身編寫繁瑣的 XML 代碼了,因此就用它。
換句話說,實際上 SOAP 擴展就是一個更好用,速度更快,專門用於處理 SOAP 服務的 HTTP 封裝庫,沒有什麼很深奧的東西。
PHP 的幫助手冊,有關於 SOAP 擴展的詳細說明文檔,已經對如何使用說得很清楚了,特別手冊後面的一些用戶的評論和貢獻的代碼片斷,基本上能夠解決你大部分的問題。下面記錄一下本身在開發過程當中遇到的一些問題,以及解決的方法和一些須要注意的地方。
如今基本上全部 SOAP Web 服務都提供 WSDL 接口描述文件,因此 non-WSDL 這種模式基本上不用考慮。
PHP SOAP 擴展同時支持 SOAP 1.1 和 SOAP 1.2 兩個版本。通常來講,如今的接口基本上也同時支持這兩個 SOAP 協議版本進行通訊,那麼在這種狀況下,固然是採用高版本的 SOAP 1.2 了。實際上,不管你是使用哪一個版本,若是你是使用 SOAP 擴展來調用服務的話,在使用該擴展過程當中沒有任何區別,對你來講都是同樣的。惟一須要你去作的是在 SoapCient
進行初始化時,把 soap_version 設置爲 SOAP_1_1 或 SOAP_1_2 便可,就像下面這樣:
// SOAP 1.1 $client = new SoapClient($wsdl, [ 'soap_version' => SOAP_1_1 ]); // SOAP 1.2 $client = new SoapClient($wsdl, [ 'soap_version' => SOAP_1_2 ]);
上面已經說了,如今的服務基本上都提供 WSDL 描述文件,若是是這樣的話,這兩個類 SoapParam
和 SoapVar
你基本上能夠不用管,由於 SOAP 之因此提供這兩個類,主要仍是爲了 PHP SOAP 擴展可以去使用一些沒有 WSDL 描述文件的服務,固然這種狀況基本上已經不存在了。
該方法也同樣,通常都它只會用於 non-WSDL 模式下,由於在 WSDL 模式下,徹底能夠把你須要調用的方法做爲 SoapClient
對象的一個方法進行調用。不過,若是調用方法的 uri 與 默認的 uri 不同時,又或者調用該方法時,你必須爲它帶上一個 SOAP Header 時,就須要使用 __soapCall
方法了。下面是摘自官方手冊的一段話:
This is a low level API function that is used to make a SOAP call. Usually, in WSDL mode, SOAP functions can be called as methods of the SoapClient object. This method is useful in non-WSDL mode when soapaction is unknown, uri differs from the default or when sending and/or receiving SOAP Headers.
__soapCall
在使用上與經過方法名直接調用的方式有一些區別。下面咱們來看看幾個例子。一樣的,咱們仍是使用一個網絡上能夠無償使用的 SOAP 服務配合咱們,該服務的主要做用是經過 QQ 號來查詢該用戶的在線狀態。服務地址
下面是請求該服務時,應該發送的 SOAP 消息:
<?xml version="1.0" encoding="utf-8"?> <soap12:Envelope xmlns:soap12="http://www.w3.org/2003/05/soap-envelope"> <soap12:Body> <qqCheckOnline xmlns="http://WebXml.com.cn/"> <qqCode>string</qqCode> </qqCheckOnline> </soap12:Body> </soap12:Envelope>
經過方法名調用該接口:
<?php $wsdl = 'http://www.webxml.com.cn/webservices/qqOnlineWebService.asmx?wsdl'; $client = new SoapClient($wsdl, [ 'soap_version' => SOAP_1_2 ]); $client->qqCheckOnline([ 'qqCode' => 8698053 ]));
使用 __soapCall
方法調用該接口:
<?php $wsdl = 'http://www.webxml.com.cn/webservices/qqOnlineWebService.asmx?wsdl'; $client = new SoapClient($wsdl, [ 'soap_version' => SOAP_1_2 ]); $client->__soapCall('qqCheckOnline', [ ['qqCode' => 8698053] ]);
重點關注兩種調用方式時,參數的不同。經過方法名直接調用的方式,參數是一維數組,而經過 __soapCall
方法調用時,參數是二維數組,這是它們之間的區別之一。
還有第二個區別,就是 __soapCall
方法能夠在調用接口時,添加額外的 SOAP Header,好比這樣:
$wsdl = 'http://www.example.com/service.asmx?wsdl'; $client = new SoapClient($wsdl, [ 'soap_version' => SOAP_1_2 ]); $auth = ['sAuthenticate' => 'ab3cde34f5r4545g']; $namespace = 'http://www.example.com'; $header = new SoapHeader($namespace, 'AuthenHeader', $auth, false); $client->__soapCall("SomeFunction", $parameters, null, $header);
雖然 SoapClient
也有 __setSoapHeaders
方法,可是它會給該實例的全部方法都添加上 SOAP Header,若是存在有些方法須要 SOAP Header 而有些又不須要的話,那麼就必須使用 __soapCall
方法,針對某個方法來添加 SOAP Header 了。
實際上,在 WSDL 模式下,若是不須要發送 SOAP Header 的話,那麼 namespace
是用不上的,由於 namespace
實際上已經在 WSDL 文件中有所描述了,PHP 的 SOAP 擴展會自動把它從 WSDL 文件中解析出來,用於構造 SOAP 請求。若是 SOAP 消息中,須要添加 SOAP Header 的話,那麼必須提供 namespace
。舉個例子:
好比說,有一個服務它須要你發送的 SOAP 消息中必須有 SOAP Header,像下面同樣:
<?xml version="1.0" encoding="utf-8"?> <soap12:Envelope xmlns:soap12="http://www.w3.org/2003/05/soap-envelope"> <soap12:Header> <AuthenHeader xmlns="http://www.example.cn"> <sAuthenticate>string</sAuthenticate> </AuthenHeader> </soap12:Header> <soap12:Body> <GetUserInfoById xmlns="http://www.example.cn"> <UserID>int</UserID> </GetUserInfoById> </soap12:Body> </soap12:Envelope>
下面是構造該 SOAP 請求的代碼:
<?php $wsdl = 'http://www.example.com/service.asmx?wsdl'; $client = new SoapClient($wsdl, [ 'soap_version' => SOAP_1_2 ]); $auth = ['sAuthenticate' => 'ab3cde34f5r4545g']; $namespace = 'http://www.example.com'; $header = new SoapHeader($namespace, 'AuthenHeader', $auth, false); $client->__setSoapHeaders($header); $response = $client->GetUserInfoById([ 'UserID' => 100 ]);
能夠看到,使用 SoapHeader
來構建一個 SOAP Header 時,必須提供 namespace
,並且是正確的命名空間。
其實,構造一個 SOAP Header 的方法不止這一種寫法,還有其餘寫法,好比你還能夠這樣構造與上面同樣的 SOAP 消息:
<?php class AuthenHeader { private $sAuthenticate; public function __construct($auth) { $this->sAuthenticate = $auth; } } $wsdl = 'http://www.example.com/service.asmx?wsdl'; $client = new SoapClient($wsdl, [ 'soap_version' => SOAP_1_2 ]); $auth = 'ab3cde34f5r4545g'; $namespace = 'http://www.example.com'; $authenHeader = new AuthenHeader($auth); $header = new SoapHeader($namespace, 'AuthenHeader', $authenHeader, false); $client->__setSoapHeaders($header); $response = $client->GetUserInfoById([ 'UserID' => 100 ]);
關於 SoapHeader
其餘更多的用法,推薦翻閱 PHP 手冊中的 SOAP 章節。
服務端在處理客戶端請求發生錯誤時,將會拋出 SoapFault 異常。對於 SOAP 擴展中,哪些方法可能會拋出異常能夠查看手冊。一旦發生了異常,咱們都應該捕捉它們,並妥善處理。像下面這樣:
try { $client = new SoapClient($wsdl, [ 'trace' => true, 'soap_version' => SOAP_1_2 ]); ..... } catch(SoapFault $e) { //在這裏處理異常 }
這兩個方法能夠查看最近一次請求和響應的內容,這兩個方法對於調試頗有幫助。固然,這兩個方法只有在 SoapClient
實例化時,trace
參數設置爲 true
纔會生效。好比像這樣:
<?php $wsdl = 'http://www.webxml.com.cn/webservices/qqOnlineWebService.asmx?wsdl'; $client = new SoapClient($wsdl, [ 'trace' => true, 'soap_version' => SOAP_1_2 ]); $client->qqCheckOnline([ 'qqCode' => 8698053 ])); echo $client->__getLastRequest(); echo $client->__getLastResponse();
getLastRequest() 輸出的內容:
<?xml version="1.0" encoding="UTF-8"?> <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://WebXml.com.cn/"> <env:Body> <ns1:qqCheckOnline> <ns1:qqCode>8698053</ns1:qqCode> </ns1:qqCheckOnline> </env:Body> </env:Envelope>
getLastResponse() 輸出的內容:
<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body> <qqCheckOnlineResponse xmlns="http://WebXml.com.cn/"> <qqCheckOnlineResult>Y</qqCheckOnlineResult> </qqCheckOnlineResponse> </soap:Body> </soap:Envelope>
在調試 SOAP 服務接口時,咱們可使用功能強大的 SoapUI 工具,能夠很方便地調試接口。
上面都是本身在學習 PHP SOAP 擴展時的一些零散的筆記,若是有不對的地方,但願你們指出,謝謝。(本文已存檔 GitHub)