【问题标题】:SOAP XML request using PHP is not working - probably headers problem使用 PHP 的 SOAP XML 请求不起作用 - 可能是标头问题
【发布时间】:2019-01-23 16:42:02
【问题描述】:

我需要使用 SOAP 和 XML 发布到客户端的 URL(如果客户端在 .Net 平台上很重要)。请求需要如下所示:

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:tem="http://tempuri.org/" xmlns:XXX="http://schemas.datacontract.org/2004/07/XXXAPI.Entities.XXX" xmlns:XXX1="http://schemas.datacontract.org/2004/07/XXXAPI.Entities.Admin"> <soap:Header/>    <soap:Body>
      <tem:SaveXXXStatus>
         <!--Optional:-->
         <tem:req>
            <!--Optional:-->
            <XXX:AWBNumber>69184678146</XXX:AWBNumber>
            <!--Optional:--> 
               … etc. …
            <!--Optional:-->
            <XXX:pincode></XXX:pincode>
         </tem:req>
         <!--Optional:-->
         <tem:profile>
            <!--Optional:-->
            <XXX1:Api_type>S</XXX1:Api_type>
            <!--Optional:-->
            <XXX1:Area></XXX1:Area>
            <!--Optional:-->
            <XXX1:LicenceKey>xxxxxxxxxxxxxxxxxxx</XXX1:LicenceKey>
            <!--Optional:-->
            <XXX1:LoginID>XXXYYY</XXX1:LoginID>
            <!--Optional:-->
            <XXX1:Version>1</XXX1:Version>
         </tem:profile>
      </tem:SaveXXXStatus>    </soap:Body> </soap:Envelope>

我正在使用以下代码:

        $ch = curl_init();
        //var_dump($ch);

        curl_setopt($ch, CURLOPT_URL,"https://example.com?wsdl");
        curl_setopt($ch, CURLOPT_HTTPHEADER, array(
            'tem:"http://tempuri.org/"',
            'Content-Type: text/xml',
            'XXX:"http://schemas.datacontract.org/2004/07/XXXAPI.Entities.XXX"'
            'XXX1:"http://schemas.datacontract.org/2004/07/XXXAPI.Entities.Admin"'
        ));
        //curl_setopt($ch, CURLOPT_USERPWD, "XXXYYY:xxxxxxxxxxxxx"); //Probably not needed
    curl_setopt($ch, CURLOPT_POST, 1);
        $strRequest = "";
        $strRequest .= "AWBNumber=69184678161";
… etc….
        $strRequest .= "&pincode=";
        $strRequest .= "&Api_type=S";
        $strRequest .= "&Area=";
        $strRequest .= "&LicenceKey=xxxxxxxx";
        $strRequest .= "&LoginID=XXXYYY";
        $strRequest .= "&Version=1";
        curl_setopt($ch, CURLOPT_POSTFIELDS,$strRequest);

        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $server_output = curl_exec($ch);
        var_dump($server_output);

This post 表明标题(信封等)可以简单地添加到 POSTFIELDS 字符串中,但我试过了,但没有用。此外,它看起来像一个黑客!

无论如何,没有任何组合有效 - 结果我得到一个零长度字符串($server_output)。传递标头的正确方法是什么,这里还需要修复什么?

【问题讨论】:

  • 为什么不使用 PHP SoapClient 类中的构建?将对象作为实体处理会更容易,就像通过 curl 发送字符串一样。

标签: php xml soap php-curl


【解决方案1】:

前几天我也卡在这个了……不过这样试了也有结果

$xml_post_string ='<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:tem="http://tempuri.org/" xmlns:XXX="http://schemas.datacontract.org/2004/07/XXXAPI.Entities.XXX" xmlns:XXX1="http://schemas.datacontract.org/2004/07/XXXAPI.Entities.Admin"> <soap:Header/>    <soap:Body>
  <tem:SaveXXXStatus>
     <!--Optional:-->
     <tem:req>
        <!--Optional:-->
        <XXX:AWBNumber>69184678146</XXX:AWBNumber>
        <!--Optional:--> 
           … etc. …
        <!--Optional:-->
        <XXX:pincode></XXX:pincode>
     </tem:req>
     <!--Optional:-->
     <tem:profile>
        <!--Optional:-->
        <XXX1:Api_type>S</XXX1:Api_type>
        <!--Optional:-->
        <XXX1:Area></XXX1:Area>
        <!--Optional:-->
        <XXX1:LicenceKey>xxxxxxxxxxxxxxxxxxx</XXX1:LicenceKey>
        <!--Optional:-->
        <XXX1:LoginID>XXXYYY</XXX1:LoginID>
        <!--Optional:-->
        <XXX1:Version>1</XXX1:Version>
     </tem:profile>
  </tem:SaveXXXStatus>    </soap:Body> </soap:Envelope>';

 $headers = array(
            "Content-type: text/xml;charset=\"utf-8\"",
            "Accept: text/xml",
            "Cache-Control: no-cache",
            "Pragma: no-cache",
            "SOAPAction: url",
            "Content-length: " . strlen($xml_post_string),
        );
$ch = curl_init();
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
        curl_setopt($ch, CURLOPT_URL, 'yoururl');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml_post_string);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        $response = curl_exec($ch);
print_r($response);

【讨论】:

  • “SOAPAction: url”中的url是什么
【解决方案2】:

正如我在 cmets 中已经提到的,使用 PHP SoapClient 中的构建类和对象作为实体会容易得多。这是一个小例子,你可以如何解决你的问题。

PHP Soap 客户端

PHP 在SoapClient 中有自己的构建版本,运行良好。看看如何初始化客户端进行开发。

try {
    $client = new \SoapClient('https://example.com?wsdl', [
        'cache_wsdl' )> WSDL_CACHE_NONE,
        'exceptions' => true,
        'trace' => true,
    ]);
} catch (\SoapFault $fault) {
    echo "<pre>";
    var_dump($fault->getMessage());
    echo "</pre>";

    if ($client) {
        echo "<pre>";
        var_dump($client->__getLastRequest(), $client->__getLastResponse());
        echo "</pre>";
    }
}

这是带有开发选项的SoapClient 类的简单初始化。将trace 选项设置为true 可以使用客户端内部函数__getLastRequest()__getLastResponse()。因此,您可以看到客户端发送了什么以及响应的样子(如果有的话)。我正在使用它来检查客户端发送的 xml。

soap 可以使用的作为对象的简单实体

SOAP 将自身定义为复杂和简单的类型定义。如果您调用客户端自己的__getTypes() 函数,您可以看到这一点。会显示很多结构体和简单类型定义,它们存储在给定的 wsdl 文件或 wsdl 文件中提到的 xsd 文件中。有了这些信息,我们就可以构建自己的对象。在这个例子中,我使用了简单的stdClass 对象。在生产方式中,您应该使用计算自己的对象。

$req = new \stdClass();
$req->AWBNumber = new \SoapVar(
    69184678146, 
    XSD_INT, 
    null, 
    null, 
    'AWBNumber', 
    'http://schemas.datacontract.org/2004/07/XXXAPI.Entities.XXX'
);
$encodedReq = new \SoapVar($req, SOAP_ENC_OBJECT, null, null, 'req', 'http://tempuri.org/');

$saveXXXStatus = new \stdClass();
$saveXXXStatus->req = $encodedReq;
$encodedSaveXXXStatus = new \SoapVar($saveXXXStatus, SOAP_ENC_OBJECT, null, null, 'SaveXXXStatus, 'http://tempuri.org/');

// send the content with the soap client
$result = $client->SaveXXXStatus($encodedSaveXXXStatus);

请记住,这是一个不完整的简短示例,会导致肥皂故障。但我在这里做了什么? xml 中的 req 节点是一个对象。您将在上面提到的__getTypes() 函数输出中找到该对象的定义。在本例中,我将此对象编译为具有AWBNumber 属性的stdClassAWBNumber 本身就是一个 SoapVar 对象。我们使用soap var是因为soap客户端使用的命名空间。定义属性后,我们将req 对象编码为soap 对象,它也是SoapVar 实例。

毕竟我们用编码参数调用了webservice方法SaveXXXStatus

最后的请求

如果您发送此示例,最后一个请求应如下所示:

<ns1:envelope xmlns:ns1="http://www.w3.org/2003/05/soap-envelope" 
    xmlns:ns2="http://tempuri.org/"
    xmlns:ns3="http://schemas.datacontract.org/2004/07/XXXAPI.Entities.XXX">
    <ns1:body>
        <ns2:SaveXXXStatus>
            <ns2:req>
                <ns3:AWBNumber>69184678146</ns3:AWBNumber>
            </ns2:req>
        </ns2:SaveXXXStatus>
    </ns1:body>
</ns1:envelope>

正如我之前所说,这只是一个示例。您必须将所有节点编码为 SoapVar 对象并将其附加到父节点,最后使用完整的编码数据调用 webservice 方法。

很简单,嗯?

【讨论】:

  • 不简单!还在努力消化。某处是否有完整/完整的示例或教程?我有大约 40 个要传递的参数 - 我需要将每个参数都创建为一个对象吗?另外,为什么只使用一个架构 URL?我们不需要一秒钟还是会产生错误?
  • 有像 wsdl2php 这样的生成器,它可以从 wsdl 文件中以 php 类的形式提供所需的实体。但这不会改变用 php 处理soap 的方式。您只需要 wsdl 文件的 URL。 wsdl 文件中提供了所有其他名称空间。如果你必须传递40个参数,你必须考虑如何做到最简单的方法。简而言之:是的,在使用 soap 和命名空间时,您必须将每个成员作为 soapvar 实例传递给相应的实体对象。
猜你喜欢
  • 2020-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多