理解多部分消息的PHP SOAP客户端?

时间:2009-07-13 21:52:28

标签: php web-services soap

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

7 个答案:

答案 0 :(得分:12)

本机PHP SoapClient类不支持多部分消息(并且在所有WS- *事务中受到很大限制)而且我认为既不是PHP编写的库NuSOAP也不是{{3}可以处理这种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客户端库。第一个也是唯一一个我想到的是Zend_Soap,其中包括SOAP MTOM,WS-Addressing,WS-Security,WS-SecurityPolicy,WS-Secure Conversation和WS-ReliableMessaging,代价是必须安装本机PHP扩展。

答案 1 :(得分:3)

使用S. Gehrig的第二个想法在这里工作得很好。

大多数情况下,您只有一条消息打包到MIME MultiPart消息中。在这些情况下,“ SoapFault异常:[客户端]看起来我们没有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;
    }
}

答案 2 :(得分:3)

遵循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);
?>

答案 3 :(得分:2)

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

<html>
<head>
    <script>
        function toggle_visibility(id) {
            var e = document.getElementById('hamburgermenu');
            if(e.style.display == 'block') 
                e.style.display = 'none';
            else
                e.style.display = 'block';
            }
    </script>

    <style>
        #hamburgermenu {
            display: none;
            position: absolute;
            z-index: 1000000; 

            height: 100%;
            width: 100%;
            margin-top: 50px;
            background-color: rgba(0,0,0,.7);
        }
    </style>
</head>

<body>
    <!-- HTML BUTTON FOR HIDE AND SHOW -->
    <button onclick="toggle_visibility('hamburgermenu');">
        <span class="glyphicon glyphicon-option-horizontal"></span>
    </button>
    <!-- HTML BUTTON FOR HIDE AND SHOW -->

    <!-- HTML MOBILE MENU -->
    <div id="hamburgermenu" >
        <ul class="mobilemenu">
            <li><a href="#">PROJECTEN</a></li>
            <li><a href="#">SKILLSET</a></li>
            <li><a href="#">STAGE</a></li>
            <li><a href="#">OVER MIJ</a></li>
            <li><a href="#">CONTACT</a></li>
        </ul>
    </div>
    <!-- END HTML MOBILE MENU -->
</body>

这仅适用于使用选项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; } } 初始化SoapClientExtended

答案 4 :(得分:1)

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

    --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;
}

答案 5 :(得分:0)

该代码也适用于我,选项“trace =&gt; true”。

感谢分享!

答案 6 :(得分:0)

更简单的恕我直言

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;
    }
}