首先:如果您正在处理未知的 Soap 服务,请获取SoapUI 。SoapUI 将读取 WSDL 文件,并允许您查看与 Soap 方法、参数和返回值相关的几乎所有内容(如果您敢于调用实时服务 - 希望有一个沙箱服务器不做任何关键的事情)。
但是 SoapUI 可以通过创建一个模拟服务来帮助您,该服务接收您的请求并以您准备的预设请求进行响应。以下是我在不触及实际服务的情况下从您链接的 WSDL 到工作代码示例的方法。
设置 SoapUI
在 SoapUI 中创建一个新项目并给出 WSDL 的位置。你可以命名这个项目。
SoapUI 然后读取 WSDL 的内容并创建包含所有描述的请求的项目。之后,您会看到服务提供了哪些方法,以及必须将哪些类型的参数输入其中,就像一棵树一样。打开这棵树将为检测到的每个方法提供“请求 1”,它在免费版本中显示一些 XML(付费版本更适合填写表格),如下所示(来自vehiclePassedTest
方法):
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tr="http://tr.gov.tp.stp.ws.PassedVehicleTestService">
<soapenv:Header>
<tr:password>?</tr:password>
<tr:username>?</tr:username>
</soapenv:Header>
<soapenv:Body>
<tr:vehiclePassedTest>
<chassisNo>?</chassisNo>
<plateNo>?</plateNo>
<plateCode>?</plateCode>
<plateCategory>?</plateCategory>
<plcEmiCode>?</plcEmiCode>
<currentUserName>?</currentUserName>
</tr:vehiclePassedTest>
</soapenv:Body>
</soapenv:Envelope>
问号是您必须提供数据的地方。此请求结构必须由您使用 PHP 的 SoapClient 创建。
让我们嘲笑一下。模拟意味着 SoapUI 启动它自己的接受请求的 Soap 服务器。它通常在 127.0.0.1:8080 上运行。右键单击项目“PassedVehicleTestService”并选择“New MockService”。接受名称,按确定。
右键单击要模拟的方法。选择“添加到模拟服务”并选择上一步中创建的。回答“是”以打开模拟响应编辑器。在那里,您可以看到答案的 XML 结构,以及服务将填写的数据的问号。此服务的所有响应都允许将一个答案作为字符串,因此请编写一些将返回的好东西。如果需要,您可以稍后将其他方法添加到此服务。
右键单击“MockService 1”并选择“开始最小化”。这将启动 Soap 服务器,它将等待传入的请求。
为模拟设置 PHP SoapClient。
你需要知道两件事。首先是 WSDL 的位置。该文件包含用于请求的真实服务器的地址,但 PHP 允许覆盖它。因此,您需要的第二个信息是正在运行的模拟服务的地址。
SoapUI 在请求编辑器中显示来自 WSDL 的地址。在您的情况下http://181.24.80.32/ws/services/PassedVehicleTestService
- 模拟服务运行在http://127.0.0.1:8080/ws/services/PassedVehicleTestService
- IP 和端口被替换,路径被保留。
这导致 PHP 代码的第一行:
$options = array(
'location' => 'http://127.0.0.1:8080/ws/services/PassedVehicleTestService',
);
$client = new SoapClient("http://www.quickregistration.ae/temp/PassedVehicleTestService.xml", $options);
之后,您有一个配置好的 SoapClient 能够与您的模拟服务对话。如果稍后您想使用真正的服务,请将带有“位置”的行从数组中删除,如果您碰巧添加了一些配置参数,请保留其他配置参数。
与模拟对话
有了客户端,你就可以做到$client->nameOfSoapMethod(paramStructure)
。参数结构通常是数组或对象和标量值(如字符串)的混合。所以这就是我首先尝试的:
$result = $client->vehiclePassedTest(array());
var_dump($result);
然后我查看 SoapUI 看看会发生什么。php脚本的输出:
Notice: Array to string conversion in [...]/soap.php on line 20
string(1) "?"
SoapUI 说:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://tr.gov.tp.stp.ws.PassedVehicleTestService">
<SOAP-ENV:Body>
<ns1:vehiclePassedTest>
<chassisNo>Array</chassisNo>
<plateNo/>
<plateCode/>
<plateCategory/>
<plcEmiCode/>
<currentUserName/>
</ns1:vehiclePassedTest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
那不是正确的做法。该数组被转换为字符串“Array”,并作为第一个参数放置。其余为空。很好,因为我知道soap 调用可能接受不止一个参数,这就是这些服务之一。
$result = $client->vehiclePassedTest('chassisNo', 'plateNo', 'plateCode', 'plateCategory', 'plcEmiCode', 'currentUserName');
这在 SoapUI 中正确显示,但标题仍然缺失。
将 SoapHeader 添加到请求中
SoapClient 提供了一个名为 的方法__setSoapHeaders()
,并且有一个类SoapHeader
。
描述说 setSoapHeaders() 接受一个 SoapHeader 对象或一组 SoapHeader 对象。让我们创建一个,传递它,看看会发生什么:
$username = new SoapHeader();
$client->__setSoapHeaders($username);
$result = $client->vehiclePassedTest('chassisNo', 'plateNo', 'plateCode', 'plateCategory', 'plcEmiCode', 'currentUserName');
var_dump($result);
PHP 说:Warning: SoapHeader::SoapHeader() expects at least 2 parameters, 0 given
$username = new SoapHeader('namespace', 'username', 'MyUserName');
这行得通。SoapUI 说:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://tr.gov.tp.stp.ws.PassedVehicleTestService" xmlns:ns2="namespace">
<SOAP-ENV:Header>
<ns2:username>MyUserName</ns2:username>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns1:vehiclePassedTest><chassisNo>chassisNo</chassisNo><plateNo>plateNo</plateNo><plateCode>plateCode</plateCode><plateCategory>plateCategory</plateCategory><plcEmiCode>plcEmiCode</plcEmiCode><currentUserName>currentUserName</currentUserName></ns1:vehiclePassedTest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
请注意 SOAP-ENV:Envelope 元素中的两个 xmlns 属性。原始请求要求用户名位于命名空间“tr”中——但“tr”只是定义为“xlmns:tr”的字符串的快捷方式——后面的字符串是您需要的命名空间(尽管它可能已经使用真正的服务)。
$username = new SoapHeader("http://tr.gov.tp.stp.ws.PassedVehicleTestService", 'username', 'myUser');
$password = new SoapHeader("http://tr.gov.tp.stp.ws.PassedVehicleTestService", 'password', 'yetAnotherPassword');
$client->__setSoapHeaders(array($username, $password));
如您所见,这正确定义了标题:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://tr.gov.tp.stp.ws.PassedVehicleTestService">
<SOAP-ENV:Header>
<ns1:username>myUser</ns1:username>
<ns1:password>yetAnotherPassword</ns1:password>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns1:vehiclePassedTest><chassisNo>chassisNo</chassisNo><plateNo>plateNo</plateNo><plateCode>plateCode</plateCode><plateCategory>plateCategory</plateCategory><plcEmiCode>plcEmiCode</plcEmiCode><currentUserName>currentUserName</currentUserName></ns1:vehiclePassedTest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
信封元素中没有两个命名空间,每个元素都属于命名空间“ns1”。这应该有效。
您的最终代码:
$options = array(
'location' => 'http://127.0.0.1:8080/ws/services/PassedVehicleTestService',
);
$client = new SoapClient("http://www.quickregistration.ae/temp/PassedVehicleTestService.xml", $options);
$username = new SoapHeader("http://tr.gov.tp.stp.ws.PassedVehicleTestService", 'username', 'myUser');
$password = new SoapHeader("http://tr.gov.tp.stp.ws.PassedVehicleTestService", 'password', 'yetAnotherPassword');
$client->__setSoapHeaders(array($username, $password));
$result = $client->vehiclePassedTest('chassisNo', 'plateNo', 'plateCode', 'plateCategory', 'plcEmiCode', 'currentUserName');
var_dump($result);