PHP生成指定SOAP请求遇Bad Request错误,求解决方案
PHP生成指定SOAP请求返回Bad Request的解决方案
问题背景
需要生成的目标SOAP请求如下:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:post="http://post.ondot.at" xmlns:arr="http://schemas.microsoft.com/2003/10/Serialization/Arrays" xmlns:core="http://Core.Model" xmlns:ser="http://schemas.microsoft.com/2003/10/Serialization/"> <soapenv:Header/> <soapenv:Body> <post:ImportShipment> <!--Optional:--> <post:row> <post:DeliveryServiceThirdPartyID>10</post:DeliveryServiceThirdPartyID> <post:OURecipientAddress> <post:AddressLine1>Teststrasse</post:AddressLine1> <post:AddressLine2/> <post:City>Wien</post:City> <post:CountryID>AT</post:CountryID> <post:Email/> <post:HouseNumber>1</post:HouseNumber> <post:Name1>Test Recipient</post:Name1> <post:PostalCode>1030</post:PostalCode> </post:OURecipientAddress> <post:OUShipperAddress> <post:AddressLine1>Musergasse</post:AddressLine1> <post:City>Wien</post:City> <post:CountryID>AT</post:CountryID> <post:Name1>test & test</post:Name1> <post:Name2/> <post:PostalCode>1010</post:PostalCode> </post:OUShipperAddress> <post:PrinterObject> <post:LabelFormatID>100x150</post:LabelFormatID> <post:LanguageID>PDF</post:LanguageID> <post:PaperLayoutID>A5</post:PaperLayoutID> </post:PrinterObject> </post:row> </post:ImportShipment> </soapenv:Body> </soapenv:Envelope>
尝试的PHP代码:
$parcelData = [ 'row' => [ 'DeliveryServiceThirdPartyID' => 10, 'OURecipientAddress' => [ 'AddressLine1' => 'Teststrasse', 'City' => 'Wien', 'CountryID' => 'AT', 'HouseNumber' => '1', 'Name1' => 'Test Recipient', 'PostalCode' => '1030', ], 'OUShipperAddress' => [ 'AddressLine1' => 'Musergasse', 'City' => 'Wien', 'CountryID' => 'AT', 'Name1' => 'test & test', 'PostalCode' => '1010', ], 'PrinterObject' => [ 'LabelFormatID' => '100x150', 'LanguageID' => 'PDF', 'PaperLayoutID' => 'A5', ], ], ]; $response = $this->client->ImportShipment($parcelData);
执行后始终返回Bad Request错误。
解决方案
1. 启用调试模式对比实际请求
开启SoapClient的trace功能,打印实际发送的XML,和目标请求对比差异,这是排查SOAP问题最直接的方法:
// 初始化客户端时开启trace和异常捕获 $client = new SoapClient('你的WSDL地址', [ 'trace' => true, 'exceptions' => true, 'encoding' => 'UTF-8', ]); try { $response = $client->ImportShipment($parcelData); } catch (SoapFault $e) { // 打印实际发送的请求XML echo "实际请求XML:\n" . htmlspecialchars($client->__getLastRequest()) . "\n"; // 打印错误信息 echo "错误详情: " . $e->getMessage(); }
2. 补全可选空节点
目标请求中包含<post:AddressLine2/>、<post:Email/>等空节点,部分SOAP服务要求显式传递这些可选字段的空值,而非省略。调整参数结构:
$parcelData = [ 'row' => [ 'DeliveryServiceThirdPartyID' => 10, 'OURecipientAddress' => [ 'AddressLine1' => 'Teststrasse', 'AddressLine2' => '', // 显式传递空值 'City' => 'Wien', 'CountryID' => 'AT', 'Email' => '', // 显式传递空值 'HouseNumber' => '1', 'Name1' => 'Test Recipient', 'PostalCode' => '1030', ], 'OUShipperAddress' => [ 'AddressLine1' => 'Musergasse', 'City' => 'Wien', 'CountryID' => 'AT', 'Name1' => 'test & test', 'Name2' => '', // 显式传递空值 'PostalCode' => '1010', ], 'PrinterObject' => [ 'LabelFormatID' => '100x150', 'LanguageID' => 'PDF', 'PaperLayoutID' => 'A5', ], ], ];
3. 检查参数是否为数组类型
部分WSDL定义中,row参数要求是数组(即使仅单条数据),需将row包裹在数组中:
$parcelData = [ 'row' => [ [ 'DeliveryServiceThirdPartyID' => 10, 'OURecipientAddress' => [...], // 地址参数同前 'OUShipperAddress' => [...], 'PrinterObject' => [...], ] ] ];
4. 确认命名空间与WSDL匹配
检查WSDL中ImportShipment方法的参数命名空间,若需要指定命名空间前缀,可通过SoapVar显式声明:
$rowData = [ 'DeliveryServiceThirdPartyID' => 10, 'OURecipientAddress' => [...], 'OUShipperAddress' => [...], 'PrinterObject' => [...], ]; // 用SoapVar指定命名空间 $soapRow = new SoapVar($rowData, SOAP_ENC_OBJECT, null, null, 'row', 'http://post.ondot.at'); $parcelData = ['row' => $soapRow]; $response = $client->ImportShipment($parcelData);
5. 处理特殊字符
目标请求中的test & test是XML转义后的结果,PHP数组中直接写test & test即可,SoapClient会自动完成转义,避免手动写&导致双重转义。
内容的提问来源于stack exchange,提问作者Its.Phil
相关产品推荐
相关产品推荐

