天天看點

你喜歡SOAP嗎?反正我不喜歡!

叫什麼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了~!

​​