php調用java發佈的webservice

java 使用 spring webservice 發佈的 service,由php使用SoapClient調用。

遇到有一個奇怪的現象,java調用發佈的webservice,沒有問題,使用工具SOAP UI調用,沒有問題,

同樣的php代碼調用google的天氣服務沒問題,調用我們的wsdl就不行。

經過摸索調試,發現如下問題,以及解決的過程。

 

首先發布的wsdl如下:

 

Xml代碼  收藏代碼
  1. <?xml version="1.0" encoding="UTF-8" standalone="no"?>  
  2. <wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:sch="http://www.phpxiaoxin.com/doorway" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://www.phpxiaoxin.com/doorway" targetNamespace="http://www.phpxiaoxin.com/doorway">  
  3.   <wsdl:types>  
  4.     <s:schema xmlns:dw="http://www.phpxiaoxin.com/doorway" xmlns:s="http://www.w3.org/2001/XMLSchema" attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://www.phpxiaoxin.com/doorway">  
  5.       <s:complexType name="Error">  
  6.         <s:attribute name="Code" type="s:string" use="required"/>  
  7.         <s:attribute name="Message" type="s:string" use="required"/>  
  8.       </s:complexType>  
  9.       <s:simpleType name="Status">  
  10.         <s:restriction base="s:string">  
  11.           <s:enumeration value="Successful"/>  
  12.           <s:enumeration value="Failed"/>  
  13.         </s:restriction>  
  14.       </s:simpleType>  
  15.       <s:complexType abstract="true" name="BaseRequest">  
  16.         <s:attribute name="Token" type="s:string" use="required"/>  
  17.         <s:attribute name="UserName" type="s:string" use="required"/>  
  18.         <s:attribute name="Password" type="s:string" use="required"/>  
  19.       </s:complexType>  
  20.       <s:complexType abstract="true" name="BaseResponse">  
  21.         <s:sequence>  
  22.           <s:element minOccurs="0" name="Error" type="dw:Error"/>  
  23.         </s:sequence>  
  24.         <s:attribute name="Token" type="s:string" use="required"/>  
  25.         <s:attribute name="Status" type="dw:Status" use="required"/>  
  26.       </s:complexType>  
  27.       <s:complexType name="StatusResponse">  
  28.         <s:complexContent>  
  29.           <s:extension base="dw:BaseResponse"/>  
  30.         </s:complexContent>  
  31.       </s:complexType>  
  32.         <s:attribute name="RatePlanCode" type="s:string" use="required"/>  
  33.         <s:attribute name="RoomTypeCode" type="s:string" use="required"/>  
  34.         <s:attribute name="NumberOfUnits" type="s:int" use="required"/>  
  35.         <s:attribute name="HotelCode" type="s:string" use="required"/>  
  36.         <s:attribute name="DistributorReservationId" type="s:string" use="required"/>  
  37.       </s:complexType>  
  38.       <s:element name="PingRequest">  
  39.         <s:complexType>  
  40.           <s:complexContent>  
  41.             <s:extension base="dw:BaseRequest">  
  42.               <s:attribute name="Echo" type="s:string" use="required"/>  
  43.             </s:extension>  
  44.           </s:complexContent>  
  45.         </s:complexType>  
  46.       </s:element>  
  47.       <s:element name="PingResponse">  
  48.         <s:complexType>  
  49.           <s:complexContent>  
  50.             <s:extension base="dw:StatusResponse">  
  51.               <s:attribute name="Echo" type="s:string" use="required"/>  
  52.             </s:extension>  
  53.           </s:complexContent>  
  54.         </s:complexType>  
  55.       </s:element>  
  56.     </s:schema>  
  57.   </wsdl:types>  
  58.   <wsdl:message name="PingResponse">  
  59.     <wsdl:part element="tns:PingResponse" name="PingResponse">  
  60.     </wsdl:part>  
  61.   </wsdl:message>  
  62.   <wsdl:message name="PingRequest">  
  63.     <wsdl:part element="tns:PingRequest" name="PingRequest">  
  64.     </wsdl:part>  
  65.   </wsdl:message>  
  66.   <wsdl:portType name="Doorway">  
  67.     <wsdl:operation name="Ping">  
  68.       <wsdl:input message="tns:PingRequest" name="PingRequest">  
  69.     </wsdl:input>  
  70.       <wsdl:output message="tns:PingResponse" name="PingResponse">  
  71.     </wsdl:output>  
  72.     </wsdl:operation>  
  73.   </wsdl:portType>  
  74.   <wsdl:binding name="DoorwaySoap11" type="tns:Doorway">  
  75.     <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>  
  76.     <wsdl:operation name="Ping">  
  77.       <soap:operation soapAction=""/>  
  78.       <wsdl:input name="PingRequest">  
  79.         <soap:body use="literal"/>  
  80.       </wsdl:input>  
  81.       <wsdl:output name="PingResponse">  
  82.         <soap:body use="literal"/>  
  83.       </wsdl:output>  
  84.     </wsdl:operation>  
  85.   </wsdl:binding>  
  86.   <wsdl:service name="HotelDoorwayService">  
  87.     <wsdl:port binding="tns:DoorwaySoap11" name="DoorwaySoap11">  
  88.       <soap:address location="/soap/doorway/"/>  
  89.     </wsdl:port>  
  90.   </wsdl:service>  
  91. </wsdl:definitions>  

 

其中定義了一個方法"Ping" 輸入爲PingRequest 和 PingResponse 其中PingRequest包含一個echo的屬性,並集成一個BaseRequest的對象。

 

下面是使用php調用的代碼:

 

Php代碼  收藏代碼
  1. <?php  
  2. ini_set("soap.wsdl_cache_enabled""0"); // disabling WSDL cache  
  3. $wsdl="http://10.10.10.10:8888/doorway/soap/doorway/doorway.wsdl";  
  4. $soap=new SoapClient($wsdlarray'trace'=>true,'cache_wsdl'=>WSDL_CACHE_NONE, 'soap_version'   => SOAP_1_1));  
  5. $method="Ping";  
  6. $params = array('Token'=>'E30ED3AA-65DE-48F9-BEA4-BA021B119625','UserName'=>'cccc','Password'=>'pppp''Echo'=>'hello');  
  7. try{  
  8. $result=$soap->$method($params);  
  9. }catch(Exception $e) {  
  10.     echo "Exception: " . $e->getMessage();  
  11. }  
  12. //$result爲stdClass類型,因此不能使用 echo $result的方式輸出,會報錯的。  
  13. echo $result->Echo;  
  14. ?>  

 

最初我們調用的時候獲取到的錯誤是找不到Ping方法,這個後來不確定是如何解決的。

後來加了SoapClient 的調用參數WSDL_CACHE_NONE讓soap調用的時候不緩存wsd,以避免服務器修改了wsdl無法及時更新。設置此參數後,每次調用,都會重 新load wsdl文件,因此監控java的log,將會拿到一個get請求,起初我們以爲是調用wsdl的請求,後來發現,他是拿wsdl的請求。

 

過了這關後,會遇到如下錯誤:Unable to parse URL

 

這個原因經過google之後發現問題出在wsdl上面:<soap:address location="/soap/doorway/"/>

這裏有的wsdl會:<soap:address location=""/>

這種方式使用java調用是沒有問題的,但是使用php調用就是不行。再加上錯誤的提示信息,可以理解爲,SoapClient沒有智能的解析這個 location,因此無法調用到soap的地址(這裏稱之爲地址,其實我也不知道是什麼意思,要想搞懂的可以看wsdl的協議。)

 

按照往上介紹的方法,在SoapClient調用裏面增加參數:location =>"http://www.phpxiaoxin.com/soap/doorway"

但這種方法顯然是不太好的,更好的方法是在生成的wsdl中就將location參數直接設置成絕對的url地址不要是相對的,也不要是空。

 

另外需要注意一個細節就是soap_version,分爲:SOAP_1_1,SOAP_1_2 這兩者發送的header是不一樣的,一個是:text/xml一個是soap/xml,有的時候不兼容,就會將請求拒絕掉。

 

最後還有一個問題就是組裝調用方法參數:

所調用方法應該組裝的參數,主要看方法對應的wsdl中的input,對於Ping方法來說,就是PingRequest。

pingRequest繼承了一個BaseRequest,那麼相當於PingRequest有:token、username、password,以及自身的echo,因此將這些屬性直接組裝成array塞進去就ok了。

 

這裏沒有實驗過一個方法對應多個輸入對象應該如何處理,大家可以自己去試。而對已簡單到String類型的輸入參數來說,我這邊的param爲:$params = array('arg0'=>"hello111");

不過我的這個我使用的是另外一個wsdl,其輸入參數的wsdl爲(使用spring+cxf+aegis數據綁定生成):

 

Xml代碼  收藏代碼
  1. <xsd:complexType name="sayHello">  
  2.     <xsd:sequence>  
  3.     <xsd:element minOccurs="0" name="arg0" nillable="true" type="xsd:string"/>  
  4.     </xsd:sequence>  
  5. </xsd:complexType>  
 

大家可以看看對應關係。是否能看出點什麼規律。而對於複雜的對象,則可以層層的array進行嵌套。可以參開這個文章:PHP SOAP如何傳入複雜對象

 

 

最後提醒一下:

php使用webservice的時候,需要確認開啓了php_soap、php_curl的擴展(php.ini)

關於php的soap client 以及其option參數,可以參考官網:http://www.php.net/manual/en/soapclient.soapclient.php

 

使用php最好配置上xdebug以便可以調試,看到對象的值,相當明瞭,

SoapClient裏面有發送的request的xml一看就知道是否有問題,以及問題出在哪裏。

詳情可以看一下xdebug等文章。

 

再就是php調用的時候我增加了try,catch,並且將exception輸出,這樣輸出的結果似乎比直接跑異常要詳細。

因此建議大家遇到問題的時候可以catch一下,以便能夠看到詳細的異常信息。

 

另外如果有對spring發佈webservice以及spring+cxf發佈webservice有問題的也可以找相關文章看一下。這裏就不貼地址了。我也曾經介紹過cxf的。

 

總結一下,出錯的緣由,由於本次webservice使用了手寫xsd的方式,先手工寫出了xsd,然後再生成的接口和對象,因此導致location設置不兼容。

 

 

 

參考:

unable-parse-url

nusoap-how-to-change-content-type-of-request

終於解決了PHP調用SOAP過程中的種種問題

 

分享一個xml在線格式化的工具:http://www.shell-tools.net/index.php?op=xml_format

文章轉自http://phpxiaoxin.iteye.com/blog/1555715

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章