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


有这样的野兽吗?这很简单 SOAP客户端 与PHP一起提供的不了解多部分消息。提前致谢。


3476
2017-07-13 21:52


起源



答案:


原生PHP SoapClient class不支持多部分消息(并且在所有WS- *事务中受到很大限制)我也认为PHP编写的库都没有 的NuSOAP 也不 Zend_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扩展。


11
2017-07-14 07:09





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

2
2017-08-14 09:37



我认为“__doRequest”应该是“_doRequest”。 - understack


答案:


原生PHP SoapClient class不支持多部分消息(并且在所有WS- *事务中受到很大限制)我也认为PHP编写的库都没有 的NuSOAP 也不 Zend_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扩展。


11
2017-07-14 07:09





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

2
2017-08-14 09:37



我认为“__doRequest”应该是“_doRequest”。 - understack


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

2
2017-08-06 19:06



即使我根本不喜欢你的代码风格,你也是我用这个显式代码示例的英雄:)工作就像魅力一样。虽然我不得不把它调整一下。我会把它换成新的答案。 - func0der


虽然这里已经给出了很多答案,但我已经把一般的解决方案放在一起,记住,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;
    }
}

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


1
2017-11-29 10:36





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

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

0
2018-03-20 16:41





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

感谢分享!


0
2018-06-23 19:39