我无法发出 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>