叫什麼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 );
例如請求參數為該類型,則Client端可以這麼調用:
//複合參數:testParam//參數一:abc=linvo//參數二:def=123$ret = $client->myFun(array('abc'=>'linvo', 'def'=>'123'));
Server端可以這麼接收參數:
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了~!