叫什麼Simple Object Access Protocol,實際上一點都不Simple!
說什麼輕量級協定,從它基于XML的編碼就知道它有多臃腫!
說什麼跨平台特性,其實各個語言需要自己實作一整套SOAP!
除了給人看的接口文檔外,還需要一份給機器看的wsdl,并且接口調用前要先載入它!
有人也許會說“wsdl是基于xml的,人也可以直接閱讀啊,完全可以不需要接口文檔!”
。。。那你說說你有幾個項目是這麼幹的?尤其是外部合作的項目!
…………
唯一的好處就是調用者可以像本地一樣調用遠端函數,但這建立在複雜封裝的基礎上,一切都要标準協定,一定程度上意味着悲催的可控性和靈活性。
總之這種感覺就像從linux的開源天堂突然掉入MS的世界……
好吧,可能是我常年用PHP養成的土鼈習慣吧,高端的東西還真享受不了~
(我靠。。。誰拿拖鞋丢我?!)
---------------------------------------- 我是分割線 --------------------------------------------
吐槽完了,下面就說說這兩天用PHP使用SOAP的感受吧~
其實PHP自帶有soap擴充,但是。。。這是個略顯坑爹的擴充。
SoapServer端沒帶生成wsdl的功能,需要使用工具(如Zend)或。。。手寫 - -|||
雖然SoapClient端支援無wsdl的方式調用,但是。。。沒有wsdl你打算給誰用?難不成自娛自樂麼~
so,最後我還是用了第三方的包,沒錯,就是nusoap!
用它實作Server端,動态生成标準的wsdl位址;用戶端倒是可以使用自帶的soap擴充。
網上有一些簡單的示例,不是過于簡單,就是不完整,總之一些關鍵點經常沒有提到,遇到的很多問題最後還是通過翻源碼解決的。
完整的例子就不寫了,這裡僅對值得特别注意的地方做下mark(其他基礎知識和簡單範例請先自行google):
1、調用addComplexType建立複合類型
常用的有兩種,一種是array類型(對應php裡的索引數組),可以這麼注冊:
$server->wsdl->addComplexType(
'testParam', //複合參數名
'complexType',
'array', //這裡說明是數組
'',
'', //基本限制
array(), //xsd:element
array(
'abc' => array('name'=>'abc', 'type'=>'xsd:string'),
'def' => array('name'=>'def', 'type'=>'xsd:int')
) //xsd:attribute
);
例如請求參數為該類型,則Client端可以這麼調用:
//複合參數:testParam
//參數一:abc=linvo
//參數二:def=123
$ret = $client->myFun(array('linvo', '123'));
Server端可以這麼接收參數:
function myFun($testParam){
$param1 = $testParam[0];
$param2 = $testParam[1];
return array($param1, $param2); //假設響應參數也為該類型
}
還有一種是struct類型(對應php裡的哈希數組),可以這麼注冊:
$server->wsdl->addComplexType(
'testParam', //複合參數名
'complexType',
'struct', //這裡說明是結構體
'all', //按照什麼排序,有三個選擇all(全部)|sequence(次序)|choice(選擇)
'',
array(
'abc' => array('name'=>'abc', 'type'=>'xsd:string'),
'def' => array('name'=>'def', 'type'=>'xsd:int')
) //xsd:element
);
//複合參數:testParam
//參數一:abc=linvo
//參數二:def=123
$ret = $client->myFun(array('abc'=>'linvo', 'def'=>'123'));
function myFun($testParam){
$param1 = $testParam['abc'];
$param2 = $testParam['def'];
return array('abc'=>$param1, 'def'=>$param2); //假設響應參數也為該類型
}
注意:
無論哪種形式,均不用展現複合參數名,隻是struct形式的複合參數中的二級參數需要展現參數名。這裡如果搞錯的話Client端可能會取到NULL。
當struct形式時,Client取到的結果為Object,如果想變為數組可以強制轉換成數組。
2、中文問題
字元集問題不管在哪裡,都讓人煩躁&%¥!
如果你和我一樣使用的是UTF-8,那麼建立soap_server對象後,需要設定這兩處的字元集:
$this->server->soap_defencoding = 'UTF-8';
$this->server->xml_encoding = 'UTF-8';
現在Clinet端接收到的響應正常了,可是傳入Server端函數中的請求參數還是有問題!
通過調試發現Server擷取到的原始請求資料($data=file_get_contents("php://input");)是正常的,隻是經過soap處理($server->service($data))傳入接口函數中就不對了,看了問題出在nusoap中。
翻源碼一步步跟,看到nusoap貌似隻支援三種字元集 ISO-8859-1|US-ASCII|UTF-8,還好我使用的是UTF-8
而且它内部預設是轉成ISO-8859-1處理的!
在nusoap_parser類的nusoap_parser函數的第4個參數(我下載下傳的版本:$Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $,在6577行)是
@param string $decode_utf8 whether to decode UTF-8 to ISO-8859-1
這個值預設是true,而且應該是從server對象傳來的,那我把 $this->server->decode_utf8 = false; 後發現報錯了貌似。。。看來這個參數不僅僅影響nusoap_parser
後來索性隻把nusoap_parser處的設定為false:
6582行:$this->decode_utf8 = false;
終于OK了~!