對 SOAP 的相關學習就先告此一段落,這是最後一篇文章用來記錄下學習過程中的一些筆記和心得。
前面三篇文章分別是:
《SOAP 介紹》
《SOAP Web 服務介紹》
《PHP SOAP 延伸模組的使用》
如何理解
因為 SOAP Web 服務是基於 HTTP 協議的,發出一個 SOAP 訊息請求,實際上利用 HTTP 動詞中的 POST,然後把 SOAP 訊息放置在 HTTP body 裡面發送。簡單來說就是:每調用一次 SOAP 服務,就是發送一條 POST 請求。
下面是一次請求介面所發送的內容:
POST /webservices/qqOnlineWebService.asmx HTTP/1.1Host: www.webxml.com.cnConnection: Keep-AliveUser-Agent: PHP-SOAP/5.4.29Content-Type: application/soap+xml; charset=utf-8; action="http://WebXml.com.cn/qqCheckOnline"Content-Length: 247 8698053
我們可以看到,一次 SOAP 請求,實際上就是向伺服器端發送了一個 POST 請求。而發送的內容正是 SOAP 訊息(它表明你本次調用的介面方法以及參數等等)。
這個 POST 請求,有一些特點,比如:它發送的內容類型為:application/soap+xml,使用者代理程式為 PHP-SOAP/5.4.29 ,其他特點自己觀察,不多說。
SOAP 延伸模組的作用
實際上,既然我們知道請求一次介面只是發送一次 POST 請求,那麼我們完全可以使用一些工具或 PHP 本身內建的一些庫(比如 curl、fsockopen)類比發送 POST 請求,而不需要使用 PHP 的 SOAP 延伸模組。對,沒錯!在 PHP 還沒有提供 SOAP 延伸模組前,的確很多人也是這樣做的。
那幹嘛要用 SOAP 延伸模組呢? 因為它官網的唄,因為它用 C 語言寫的,速度杠杠的,而且封裝得很好用,也不需要自己編寫繁瑣的 XML 代碼了,所以就用它。
換句話說,實際上 SOAP 延伸模組就是一個更好用,速度更快,專門用於處理 SOAP 服務的 HTTP 封裝庫,沒有什麼很深奧的東西。
使用方法
PHP 的協助手冊,有關於 SOAP 延伸模組的詳細說明文檔,已經對如何使用說得很清楚了,特別手冊後面的一些使用者的評論和貢獻的程式碼片段,基本上可以解決你大部分的問題。下面記錄一下自己在開發過程中遇到的一些問題,以及解決的方法和一些需要注意的地方。
WSDL 和 non-WSDL
現在基本上所有 SOAP Web 服務都提供 WSDL 介面描述檔案,所以 non-WSDL 這種模式基本上不用考慮。
關於 SOAP 版本
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]);
SoapParam 和 SoapVar
上面已經說了,現在的服務基本上都提供 WSDL 描述檔案,如果是這樣的話,這兩個類 SoapParam 和 SoapVar 你基本上可以不用管,因為 SOAP 之所以提供這兩個類,主要還是為了 PHP SOAP 延伸模組能夠去使用一些沒有 WSDL 描述檔案的服務,當然這種情況基本上已經不存在了。
關於 __soapCall 方法
該方法也一樣,一般都它只會用於 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 訊息:
string
通過方法名調用該介面:
SOAP_1_2]);$client->qqCheckOnline([ 'qqCode' => 8698053]));
使用 __soapCall 方法調用該介面:
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 了。
關於命名空間(namespace)
實際上,在 WSDL 模式下,如果不需要發送 SOAP Header 的話,那麼 namespace 是用不上的,因為 namespace 實際上已經在 WSDL 檔案中有所描述了,PHP 的 SOAP 延伸模組會自動把它從 WSDL 檔案中解析出來,用於構造 SOAP 請求。如果 SOAP 訊息中,需要添加 SOAP Header 的話,那麼必須提供 namespace。舉個例子:
比如說,有一個服務它需要你發送的 SOAP 訊息中必須有 SOAP Header,像下面一樣:
string int
下面是構造該 SOAP 請求的代碼:
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 訊息:
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
服務端在處理用戶端請求發生錯誤時,將會拋出 SoapFault 異常。對於 SOAP 延伸模組中,哪些方法可能會拋出異常可以查看手冊。一旦發生了異常,我們都應該捕捉它們,並妥善處理。像下面這樣:
try { $client = new SoapClient($wsdl, [ 'trace' => true, 'soap_version' => SOAP_1_2 ]); .....} catch(SoapFault $e) { //在這裡處理異常}
getLastRequest 和 getLastResponse
這兩個方法可以查看最近一次請求和響應的內容,這兩個方法對於調試很有協助。當然,這兩個方法只有在 SoapClient 執行個體化時,trace 參數設定為 true 才會生效。比如像這樣:
true, 'soap_version' => SOAP_1_2]);$client->qqCheckOnline([ 'qqCode' => 8698053]));echo $client->__getLastRequest();echo $client->__getLastResponse();
getLastRequest() 輸出的內容:
8698053
getLastResponse() 輸出的內容:
Y
SoapUI 調試工具
在調試 SOAP 服務介面時,我們可以使用功能強大的 SoapUI 工具,可以很方便地調試介面。
總結
上面都是自己在學習 PHP SOAP 延伸模組時的一些零散的筆記,如果有不對的地方,希望大家指出,謝謝。(本文已存檔 GitHub)