【问题标题】:PHP SOAP client that understands multi-part messages?理解多部分消息的 PHP SOAP 客户端?
【发布时间】:2009-07-13 21:52:27
【问题描述】:

有这样的野兽吗? PHP 附带的简单SOAP client 不理解多部分消息。提前致谢。

【问题讨论】:

    标签: php web-services soap


    【解决方案1】:

    原生 PHP SoapClient 类不支持多部分消息(并且在所有 WS-* 事务中受到严格限制),我也认为 PHP 编写的库 NuSOAPZend_Soap 都不能处理这种类型SOAP 消息。

    我能想到两种解决方案:

    • 扩展 SoapClient 类并覆盖 SoapClient::__doRequest() 方法以获取实际响应字符串,然后您可以随心所欲地对其进行解析。

      class MySoapClient extends SoapClient
      {
          public function __doRequest($request, $location, $action, $version, $one_way = 0)
          {
              $response = parent::__doRequest($request, $location, $action, $version, $one_way);
              // parse $response, extract the multipart messages and so on
          }
      }
      

      虽然这可能有点棘手 - 但值得一试。

    • 为 PHP 使用更复杂的 SOAP 客户端库。我想到的第一个也是唯一一个是WSO2 WSF/PHP,它具有 SOAP MTOM、WS-Addressing、WS-Security、WS-SecurityPolicy、WS-Secure Conversation 和 WS-ReliableMessaging,但代价是必须安装原生 PHP 扩展.

    【讨论】:

      【解决方案2】:

      尽管这个答案已经在这里给出了很多,但我已经整理了一个通用的解决方案,记住,XML 可以在没有包装器的情况下出现。

      class SoapClientExtended extends SoapClient
      {
          /**
           * Sends SOAP request using a predefined XML
           *
           * Overwrites the default method SoapClient::__doRequest() to make it work
           * with multipart responses.
           *
           * @param string $request      The XML content to send
           * @param string $location The URL to request.
           * @param string $action   The SOAP action. [optional] default=''
           * @param int    $version  The SOAP version. [optional] default=1
           * @param int    $one_way  [optional] ( If one_way is set to 1, this method
           *                         returns nothing. Use this where a response is
           *                         not expected. )
           *
           * @return string The XML SOAP response.
           */
          public function __doRequest(
              $request, $location, $action, $version, $one_way = 0
          ) {
              $result = parent::__doRequest($request, $location, $action, $version, $one_way);
      
              $headers = $this->__getLastResponseHeaders();
      
              // Do we have a multipart request?
              if (preg_match('#^Content-Type:.*multipart\/.*#mi', $headers) !== 0) {
                  // Make all line breaks even.
                  $result = str_replace("\r\n", "\n", $result);
      
                  // Split between headers and content.
                  list(, $content) = preg_split("#\n\n#", $result);
                  // Split again for multipart boundary.
                  list($result, ) = preg_split("#\n--#", $content);
              }
      
              return $result;
          }
      }
      

      这仅在您使用选项 trace => true 初始化 SoapClientExtended 时有效。

      【讨论】:

      • 像魅力一样工作,为我节省了很多时间。谢谢!
      【解决方案3】:

      使用 S. Gehrig 的第二个想法在这里效果很好。

      大多数情况中,您只需将一条消息打包到 MIME MultiPart 消息中。在这些情况下,会引发“SoapFault 异常:[Client] 看起来我们没有 XML 文档”异常。这里下面的类应该做的很好:

      class MySoapClient extends SoapClient
      {
          public function __doRequest($request, $location, $action, $version, $one_way = 0)
          {
              $response = parent::__doRequest($request, $location, $action, $version, $one_way);
              // strip away everything but the xml.
              $response = preg_replace('#^.*(<\?xml.*>)[^>]*$#s', '$1', $response);
              return $response;
          }
      }
      

      【讨论】:

      • 我认为“__doRequest”应该是“_doRequest”。
      【解决方案4】:

      遵循 PHP 文档中 rafinskipg 的建议:

      支持 MTOM 将此代码添加到您的项目中:

      <?php 
      class MySoapClient extends SoapClient
      {
          public function __doRequest($request, $location, $action, $version, $one_way = 0)
          {
              $response = parent::__doRequest($request, $location, $action, $version, $one_way);
              // parse $response, extract the multipart messages and so on
      
              //this part removes stuff
              $start=strpos($response,'<?xml');
              $end=strrpos($response,'>');    
              $response_string=substr($response,$start,$end-$start+1);
              return($response_string);
          }
      }
      
      ?>
      

      那么你就可以这样做了

      <?php
        new MySoapClient($wsdl_url);
      ?>
      

      【讨论】:

      • 尽管我根本不喜欢你的代码风格,但你是我的英雄,这个明确的代码示例 :) 像魅力一样工作。虽然我不得不稍微调整一下。我会把它放在一个新的答案中。
      【解决方案5】:

      只是为之前建议的步骤添加更多亮点。您必须收到以下格式的回复

          --uuid:eca72cdf-4e96-4ba9-xxxxxxxxxx+id=108
      Content-ID: <http://tempuri.org/0>
      Content-Transfer-Encoding: 8bit
      Content-Type: application/xop+xml;charset=utf-8;type="text/xml"
      
      <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body> content goes here </s:Body></s:Envelope>
      --uuid:c19585dd-6a5a-4c08-xxxxxxxxx+id=108--
      

      只需使用以下代码(我对正则表达式不太擅长,所以使用字符串函数)

       public function __doRequest($request, $location, $action, $version, $one_way = 0)
      {
          $response = parent::__doRequest($request, $location, $action, $version, $one_way);
          // strip away everything but the xml.
          $response = stristr(stristr($response, "<s:"), "</s:Envelope>", true) . "</s:Envelope>";
      return $response;
      }
      

      【讨论】:

        【解决方案6】:

        更简单的恕我直言

        class SoapClientUnwrappedXml extends SoapClient
        {
        
            const SOAP_ENVELOPE_REGEXP = '/^<soap:Envelope[^>]*>(.*)<\/soap:Envelope>/m';
        
            /**
             * Sends SOAP request using a predefined XML.
             *
             * Overwrites the default method SoapClient::__doRequest() to make it work
             * with multipart responses or prefixed/suffixed by uuids.
             *
             * @return string The XML Valid SOAP response.
             */
            public function __doRequest($request, $location, $action, $version, $one_way = 0): string
            {
                $result = parent::__doRequest($request, $location, $action, $version, $one_way);
                $headers = $this->__getLastResponseHeaders();
        
                if (preg_match('#^Content-Type:.*multipart\/.*#mi', $headers) !== 0) {
                    preg_match_all(self::SOAP_ENVELOPE_REGEXP, $result, $resultSanitized, PREG_SET_ORDER, 0);
                    $result = $resultSanitized[0][0] ?? $result;
                }
        
                return $result;
            }
        }
        

        【讨论】:

          【解决方案7】:

          该代码也适用于我的选项“trace => true”。

          感谢分享!

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2015-04-10
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-07-14
            • 2012-08-27
            • 1970-01-01
            相关资源
            最近更新 更多