1

我无法发出 SOAP 请求。我正在使用生成的客户端代码,而这些代码又在 WSDL 模式下使用 SoapClient。我可以进行初始会话开始调用,但是当我进行另一个调用时,命名空间似乎不正常。

这是来自 SoapUI 的有效 SOAP 请求:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsdl="http://vendor_uri/">
   <soapenv:Header/>
   <soapenv:Body>
      <wsdl:GetProducts>
         <GetProductsRequest>
            <UserInfo Token="XXXXX"/>
            <ClientReferenceData Service="pinless_dialing"/>
         </GetProductsRequest>
      </wsdl:GetProducts>
   </soapenv:Body>
</soapenv:Envelope>

这是 SoapClient 生成的 XML 请求(通过 Wireshark):

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="" xmlns:ns2="http://vendor_uri/">
    <SOAP-ENV:Body>
        <ns2:GetProducts>
            <GetProductsRequest>
                <ns1:UserInfo Token="XXXXX"/>
                <ns1:ClientReferenceData Service="pinless_dialing"/>
            </GetProductsRequest>
        </ns2:GetProducts>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

问题似乎出在 ns1 命名空间中。如果我接受同样的请求,并去掉那个命名空间,调用就会顺利通过 SoapUI。

同样,这是由 SoapClient 直接生成的。以下是生成代码中的相关代码:

class B2BServiceSoapClient{
    /**
     * The WSDL URI
     *
     * @var string
     */
    public static $_WsdlUri='http://vendor_wsdl_uri';
    /**
     * The PHP SoapClient object
     *
     * @var object
     */
    public static $_Server=null;

    /**
     * Send a SOAP request to the server
     *
     * @param string $method The method name
     * @param array $param The parameters
     * @return mixed The server response
     */
    public static function _Call($method,$param){
        if(is_null(self::$_Server))
            self::$_Server=new SoapClient(self::$_WsdlUri);
        return self::$_Server->__soapCall($method,$param);
    }

该类的其余部分由镜像 WSDL 的类型定义组成。

这是我用来练习课程的代码:

<pre>
<?php
ini_set('soap.wsdl_cache_enabled',0);

require_once('BossILDClient.php');

$client = new B2BServiceSoapClient();
$sessionToken = new UserInfoType();
$parameters = new SessionStart();
$parameters->UserInfo = new UserInfoType();
$parameters->UserInfo->UserName = 'username';
$parameters->UserInfo->Password = 'password';

echo "Calling SessionStart(" . print_r($parameters, true) . ")" . PHP_EOL . PHP_EOL;

$result = $client->SessionStart($parameters);
$sessionToken->Token = $result->SessionStartResponse->Result->Token; 

echo "REQUEST HEADER:\n" . htmlentities(B2BServiceSoapClient::$_Server->__getLastRequestHeaders()) . "\n";
echo "REQUEST:\n" . htmlentities(B2BServiceSoapClient::$_Server->__getLastRequest()) . "\n";
echo "RESPONSE HEADER:\n" . htmlentities(B2BServiceSoapClient::$_Server->__getLastResponseHeaders()) . "\n";
echo "RESPONSE:\n" . htmlentities(B2BServiceSoapClient::$_Server->__getLastResponse()) . "\n";

$getProducts = new GetProducts();
$getProducts->GetProductsRequest = new GetProductsRequestType();
$getProducts->GetProductsRequest->ClientReferenceData = new ClientReferenceDataType();
$getProducts->GetProductsRequest->ClientReferenceData->Service = 'pinless_dialing';
$getProducts->GetProductsRequest->UserInfo = $sessionToken;

echo "Calling GetProducts(" . print_r($getProducts, true) . ")" . PHP_EOL . PHP_EOL;

try {
    $result = $client->GetProducts($getProducts);
} catch(SoapFault $sf) {
    echo "SOAP Fault error!!!" . PHP_EOL . PHP_EOL;
}

echo "REQUEST HEADER:\n" . htmlentities(B2BServiceSoapClient::$_Server->__getLastRequestHeaders()) . "\n";
echo "REQUEST:\n" . htmlentities(B2BServiceSoapClient::$_Server->__getLastRequest()) . "\n";
echo "RESPONSE HEADER:\n" . htmlentities(B2BServiceSoapClient::$_Server->__getLastResponseHeaders()) . "\n";
echo "RESPONSE:\n" . htmlentities(B2BServiceSoapClient::$_Server->__getLastResponse()) . "\n";
?>
</pre>

我已经用尽了我的疯狂猜测并考虑了无济于事的想法。我怎样才能防止那个“内部”命名空间?

这是上面代码的直接输出:

Calling SessionStart(SessionStart Object
(
    [UserInfo] => UserInfoType Object
        (
            [UserName] => username
            [Password] => password
            [Token] => 
        )

)
)

REQUEST HEADER:
POST /b2bapi/b2bservice.asmx HTTP/1.1
Host: 169.132.165.178
Connection: Keep-Alive
User-Agent: PHP-SOAP/5.4.4
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://wsdl.idt.b2bapi.net/SessionStart"
Content-Length: 300


REQUEST:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://wsdl.idt.b2bapi.net/">
  <SOAP-ENV:Body>
    <ns1:SessionStart>
      <UserInfo UserName="username" Password="password"/>
    </ns1:SessionStart>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

RESPONSE HEADER:
HTTP/1.1 200 OK
Cache-Control: private, max-age=0
Content-Length: 494
Content-Type: text/xml; charset=utf-8
Server: Microsoft-IIS/7.0
X-AspNet-Version: 4.0.30319
Set-Cookie: PSFSecKeyPart=XXXXXX; expires=Thu, 09-Aug-2012 19:56:39 GMT; path=/
X-Powered-By: ASP.NET
Date: Thu, 09 Aug 2012 19:36:39 GMT

RESPONSE:
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Body>
    <SessionStartResponse xmlns="http://wsdl.idt.b2bapi.net/">
      <SessionStartResponse xmlns="">
        <ResponseReferenceData Success="Y">
          <MessageList />
        </ResponseReferenceData>
        <Result Token="XXXXXX" />
      </SessionStartResponse>
    </SessionStartResponse>
  </soap:Body>
</soap:Envelope>

Calling GetProducts(GetProducts Object
(
    [GetProductsRequest] => GetProductsRequestType Object
        (
            [UserInfo] => UserInfoType Object
                (
                    [UserName] => 
                    [Password] => 
                    [Token] => XXXXXX
                )

            [ClientReferenceData] => ClientReferenceDataType Object
                (
                    [ClientTransactionID] => 
                    [Service] => pinless_dialing
                    [IP] => 
                    [TimeStamp] => 
                )

            [Parameters] => 
        )

)
)

SOAP Fault error!!!

REQUEST HEADER:
POST /b2bapi/b2bservice.asmx HTTP/1.1
Host: 169.132.165.178
Connection: Keep-Alive
User-Agent: PHP-SOAP/5.4.4
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://wsdl.idt.b2bapi.net/GetProducts"
Content-Length: 394
Cookie: PSFSecKeyPart=XXXXXX;


REQUEST:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="" xmlns:ns2="http://wsdl.idt.b2bapi.net/">
  <SOAP-ENV:Body>
    <ns2:GetProducts>
      <GetProductsRequest>
        <ns1:UserInfo Token="XXXXXX"/>
        <ns1:ClientReferenceData Service="pinless_dialing"/>
      </GetProductsRequest>
    </ns2:GetProducts>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

RESPONSE HEADER:
HTTP/1.1 400 Bad Request
Cache-Control: private
Transfer-Encoding: chunked
Content-Type: text/html
Server: Microsoft-IIS/7.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Thu, 09 Aug 2012 19:36:39 GMT

RESPONSE:

这是精简的 WSDL:

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://wsdl.idt.b2bapi.net/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="http://wsdl.idt.b2bapi.net/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
   <wsdl:documentation>IDT B2BService</wsdl:documentation>
   <wsdl:types>
      <s:schema elementFormDefault="qualified" targetNamespace="http://wsdl.idt.b2bapi.net/">
         <s:import/>
         <s:element name="GetProducts">
            <s:complexType>
               <s:sequence>
                  <s:element minOccurs="0" maxOccurs="1" ref="GetProductsRequest"/>
               </s:sequence>
            </s:complexType>
         </s:element>
         <s:element name="GetProductsResponse">
            <s:complexType>
               <s:sequence>
                  <s:element minOccurs="0" maxOccurs="1" ref="GetProductsResponse"/>
               </s:sequence>
            </s:complexType>
         </s:element>
         <s:element name="SessionStart">
            <s:complexType>
               <s:sequence>
                  <s:element minOccurs="0" maxOccurs="1" ref="UserInfo"/>
               </s:sequence>
            </s:complexType>
         </s:element>
         <s:element name="SessionStartResponse">
            <s:complexType>
               <s:sequence>
                  <s:element minOccurs="0" maxOccurs="1" ref="SessionStartResponse"/>
               </s:sequence>
            </s:complexType>
         </s:element>
      </s:schema>
      <s:schema elementFormDefault="qualified">
         <s:complexType name="UserInfoType">
            <s:attribute name="UserName" type="s:string"/>
            <s:attribute name="Password" type="s:string"/>
            <s:attribute name="Token" type="s:string"/>
         </s:complexType>
         <s:complexType name="ClientReferenceDataType">
            <s:attribute name="ClientTransactionID" type="s:string"/>
            <s:attribute name="Service" type="enumService" use="required"/>
            <s:attribute name="IP" type="s:string"/>
            <s:attribute name="TimeStamp" type="s:dateTime" use="required"/>
         </s:complexType>
         <s:simpleType name="enumService">
            <s:restriction base="s:string">
               <s:enumeration value="pinless_dialing"/>
               <s:enumeration value="imtu"/>
               <s:enumeration value="remittance"/>
               <s:enumeration value="virtual_visa"/>
            </s:restriction>
         </s:simpleType>
         <s:complexType name="ResponseReferenceDataType">
            <s:sequence>
               <s:element minOccurs="0" maxOccurs="1" name="MessageList" type="ArrayOfMessageType"/>
            </s:sequence>
            <s:attribute name="Success" type="s:string"/>
            <s:attribute name="TransactionID" type="s:string"/>
            <s:attribute name="ClientTransactionID" type="s:string"/>
         </s:complexType>
         <s:complexType name="ArrayOfMessageType">
            <s:sequence>
               <s:element minOccurs="0" maxOccurs="unbounded" name="Message" type="MessageType"/>
            </s:sequence>
         </s:complexType>
         <s:complexType name="MessageType">
            <s:attribute name="PlatformCode" type="s:string"/>
            <s:attribute name="Service" type="s:string"/>
            <s:attribute name="Command" type="s:string"/>
            <s:attribute name="StatusCode" type="s:string"/>
            <s:attribute name="StatusText" type="s:string"/>
         </s:complexType>
         <s:element name="GetProductsRequest" type="GetProductsRequestType"/>
         <s:complexType name="GetProductsRequestType">
            <s:sequence>
               <s:element minOccurs="0" maxOccurs="1" name="UserInfo" type="UserInfoType"/>
               <s:element minOccurs="0" maxOccurs="1" name="ClientReferenceData" type="ClientReferenceDataType"/>
               <s:element minOccurs="0" maxOccurs="1" name="Parameters" type="GetProductsParametersType"/>
            </s:sequence>
         </s:complexType>
         <s:complexType name="GetProductsParametersType">
            <s:attribute name="DataType" type="s:string"/>
         </s:complexType>
         <s:element name="GetProductsResponse" type="GetProductsResponseType"/>
         <s:complexType name="GetProductsResponseType">
            <s:sequence>
               <s:element minOccurs="0" maxOccurs="1" name="ResponseReferenceData" type="ResponseReferenceDataType"/>
               <s:element minOccurs="0" maxOccurs="1" name="ProductList" type="ArrayOfProductListTypeProduct"/>
            </s:sequence>
         </s:complexType>
         <s:complexType name="ArrayOfProductListTypeProduct">
            <s:sequence>
               <s:element minOccurs="0" maxOccurs="unbounded" name="Product">
                  <s:complexType>
                     <s:sequence>
                        <s:element minOccurs="0" maxOccurs="1" name="ProductCountryList" type="ArrayOfProductCountryTypeProductCountry"/>
                     </s:sequence>
                     <s:attribute name="Code" type="s:string"/>
                     <s:attribute name="Name" type="s:string"/>
                     <s:attribute name="DisplayName" type="s:string"/>
                     <s:attribute default="0" name="Denomination" type="s:integer"/>
                     <s:attribute name="Default" type="s:string"/>
                  </s:complexType>
               </s:element>
            </s:sequence>
         </s:complexType>
         <s:complexType name="ArrayOfProductCountryTypeProductCountry">
            <s:sequence>
               <s:element minOccurs="0" maxOccurs="unbounded" name="ProductCountry">
                  <s:complexType>
                     <s:sequence>
                        <s:element minOccurs="0" maxOccurs="1" name="CarrierList" type="ArrayOfCarrierTypeCarrier"/>
                     </s:sequence>
                     <s:attribute name="CountryCode" type="s:string"/>
                     <s:attribute name="CountryName" type="s:string"/>
                  </s:complexType>
               </s:element>
            </s:sequence>
         </s:complexType>
         <s:complexType name="ArrayOfCarrierTypeCarrier">
            <s:sequence>
               <s:element minOccurs="0" maxOccurs="unbounded" name="Carrier">
                  <s:complexType>
                     <s:attribute name="CarrierCode" type="s:string"/>
                     <s:attribute name="CarrierName" type="s:string"/>
                  </s:complexType>
               </s:element>
            </s:sequence>
         </s:complexType>
         <s:element name="UserInfo" type="UserInfoType"/>
         <s:element name="SessionStartResponse" type="SessionStartResponseType"/>
         <s:complexType name="SessionStartResponseType">
            <s:sequence>
               <s:element minOccurs="0" maxOccurs="1" name="ResponseReferenceData" type="ResponseReferenceDataType"/>
               <s:element minOccurs="0" maxOccurs="1" name="Result" type="SessionStartResultType"/>
            </s:sequence>
         </s:complexType>
         <s:complexType name="SessionStartResultType">
            <s:attribute name="Token" type="s:string"/>
         </s:complexType>
         <s:element name="ResponseReferenceData" type="ResponseReferenceDataType"/>
      </s:schema>
   </wsdl:types>
   <wsdl:message name="GetProductsSoapIn">
      <wsdl:part name="parameters" element="tns:GetProducts"/>
   </wsdl:message>
   <wsdl:message name="GetProductsSoapOut">
      <wsdl:part name="parameters" element="tns:GetProductsResponse"/>
   </wsdl:message>
   <wsdl:message name="SessionStartSoapIn">
      <wsdl:part name="parameters" element="tns:SessionStart"/>
   </wsdl:message>
   <wsdl:message name="SessionStartSoapOut">
      <wsdl:part name="parameters" element="tns:SessionStartResponse"/>
   </wsdl:message>
   <wsdl:portType name="B2BServiceSoap">
      <wsdl:operation name="GetProducts">
         <wsdl:input message="tns:GetProductsSoapIn"/>
         <wsdl:output message="tns:GetProductsSoapOut"/>
      </wsdl:operation>
      <wsdl:operation name="SessionStart">
         <wsdl:input message="tns:SessionStartSoapIn"/>
         <wsdl:output message="tns:SessionStartSoapOut"/>
      </wsdl:operation>
   </wsdl:portType>
   <wsdl:binding name="B2BServiceSoap" type="tns:B2BServiceSoap">
      <soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
      <wsdl:operation name="GetProducts">
         <soap:operation soapAction="http://wsdl.idt.b2bapi.net/GetProducts" style="document"/>
         <wsdl:input>
            <soap:body use="literal"/>
         </wsdl:input>
         <wsdl:output>
            <soap:body use="literal"/>
         </wsdl:output>
      </wsdl:operation>
      <wsdl:operation name="SessionStart">
         <soap:operation soapAction="http://wsdl.idt.b2bapi.net/SessionStart" style="document"/>
         <wsdl:input>
            <soap:body use="literal"/>
         </wsdl:input>
         <wsdl:output>
            <soap:body use="literal"/>
         </wsdl:output>
      </wsdl:operation>
   </wsdl:binding>
   <wsdl:binding name="B2BServiceSoap12" type="tns:B2BServiceSoap">
      <soap12:binding transport="http://schemas.xmlsoap.org/soap/http"/>
      <wsdl:operation name="GetProducts">
         <soap12:operation soapAction="http://wsdl.idt.b2bapi.net/GetProducts" style="document"/>
         <wsdl:input>
            <soap12:body use="literal"/>
         </wsdl:input>
         <wsdl:output>
            <soap12:body use="literal"/>
         </wsdl:output>
      </wsdl:operation>
      <wsdl:operation name="SessionStart">
         <soap12:operation soapAction="http://wsdl.idt.b2bapi.net/SessionStart" style="document"/>
         <wsdl:input>
            <soap12:body use="literal"/>
         </wsdl:input>
         <wsdl:output>
            <soap12:body use="literal"/>
         </wsdl:output>
      </wsdl:operation>
   </wsdl:binding>
   <wsdl:service name="B2BService">
      <wsdl:documentation>IDT B2BService</wsdl:documentation>
      <wsdl:port name="B2BServiceSoap" binding="tns:B2BServiceSoap">
         <soap:address location="http://169.132.165.178/b2bapi/b2bservice.asmx"/>
      </wsdl:port>
      <wsdl:port name="B2BServiceSoap12" binding="tns:B2BServiceSoap12">
         <soap12:address location="http://169.132.165.178/b2bapi/b2bservice.asmx"/>
      </wsdl:port>
   </wsdl:service>
</wsdl:definitions>
4

1 回答 1

3

我的猜测是这SoapClient很困惑,因为它elementFormDefault="qualified"在架构上看到但没有命名空间。将 WSDL 保存到您的本地计算机并尝试<xsd:schema>通过删除所有以下内容使所有内容成为 WSDL 中第一个元素的一部分:

</s:schema>
<s:schema elementFormDefault="qualified">

之后将您的本地 WSDL 传递给SoapClient. WSDL 中的端点应该足以告诉它将消息发送到哪里。

于 2012-08-10T12:46:13.087 回答