【问题标题】:Asynchronous PHP SOAP server send Acknowledgement message before response?异步PHP SOAP服务器在响应之前发送确认消息?
【发布时间】:2018-05-09 14:02:17
【问题描述】:

我不确定该怎么做。我正在编写的 SOAP 服务的规范表明它需要在响应请求的响应之前发送回确认消息。

这是如何在 PHP 中完成的?我没有看到如何执行此操作的示例。

来自需求文档:

集成合作伙伴向供应商发送一条确认消息 对于每个 SubmitInv 消息请求。单个确认消息 供应商也从每个地方发送给集成合作伙伴 RequestInv 消息响应

这不是标准的 TCP 确认响应。这是一个自定义 SOAP 格式的响应,是他们确认请求已收到。请参见下面的示例。

询问供应商后:

他们声称这是一个遗留系统,它是为处理而编写的 那个流。他们目前无法改变它。我告诉他在 20 多年的编程经验,我从未见过任何 SOAP 系统需要 ACK。 他声称这与不得不“等待”回应有关。 显然他们不明白如何正确处理无状态 处理。

我已经尝试使用 FoxVSky 下面概述的 PHP 输出缓冲函数来完成它,它在 SOAP 事务中不起作用。 此外,PHP 内置的标准 SOAP 库和 Zend SOAP 库都具有执行此操作的功能。

示例:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" 
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
               xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Body>
    <PAddRs>
      <RqUID>f11958c8-3fde-42ca-bd94-94fdfca316ef</RqUID>
      <PKey>46dba062-2105-4851-831f-a1d364741329</PKey>
      <AppStatus>
        <AppStatusCode>Accept</AppStatusCode>
      </AppStatus>
    </PAddRs>
  </soap:Body>
</soap:Envelope>

【问题讨论】:

  • 确切的措辞是什么?你确定他们不是说他们需要某种 ping 端点来检查连接状态吗?
  • One acknowledgement message is sent by Integration Partner to Vendor for every SubmitInv message request. A single acknowledgement message is also sent by Vendor to the Integration Partner from every RequestInv message response.
  • 这是在支付集成环境中还是其他什么?
  • 不,库存搜索供应商。
  • 该服务将处理RequestInv 请求并以SubmitInv 响应响应该请求。

标签: php soap


【解决方案1】:

好的,我已经在我的 SOAP 服务中实现了确认消息,这是从客户端调用它的方式:

<?php
require_once __DIR__ . '/vendor/autoload.php';
$options = array();
$options['cache_wsdl'] = WSDL_CACHE_NONE;
$options['soap_version'] = SOAP_1_2;
$client = new Zend\Soap\Client("http://localhost/soap/server.php?wsdl", $options);

try {
    // Currently loading example request
    $xml = simplexml_load_file('RequestExample.xml');
    $t_xml = new DOMDocument();
    $t_xml->loadXML($xml->asXML());
    $xml = $t_xml->saveXML($t_xml->documentElement);
    $response = $client->ReqInv($xml);
} catch (Exception $e) {
    $response = 'Exception: '. $e. "\n"; 
}
echo $response;    

还有我的服务:

<?php
require_once __DIR__ . '/vendor/autoload.php';
require(__DIR__ . '/PTResp.php');

use Zend\Soap\AutoDiscover;
use Zend\Soap\Server;
use Zend\Soap\Wsdl;

class PT {
    /**
     * function ReqInv
     * Function to return the inventory for the passed request.
     * 
     *  @param string $request 
     *  @return string
     */
    function ReqInv($request) {
        $pt = new PTResp($request);
        return $pt->toString();
    }   
}

if (isset($_GET['wsdl'])) {
    $wsdl = new AutoDiscover();
    $wsdl->setUri('http://localhost/soap/server.php');
    $wsdl->setClass('PT');
    $wsdl->handle();
} else {
    $server = new Zend\Soap\Server('http://localhost/soap/server.php?wsdl');
    $server->setClass('PT');
    $server->setEncoding('ISO-8859-1');
    $server->handle();
}

还有我的班级(在 PTResp.php 中):

class PT {

    function __construct($xml) {
        $this->m = new Mustache_Engine;
        $this->xml = @simplexml_load_string($xml);
        $this->xml->registerXPathNamespace(<my namespace info>);
        $this->SendAck();
        $this->BuildResponse();
    } // function __construct

    /*
    * This is the function that is actually called to return the response to the client.
    */
    function toString() {
        $domxml = new DOMDocument('1.0');
        $domxml->preserveWhiteSpace = false;
        $domxml->formatOutput = true;
        $domxml->loadXML($this->response);
        $this->response = $domxml->saveXML($domxml->documentElement);
        return $this->response;
    } // function toString    

    function SendAck() {        
        $this->Status = "Accept";
        $xml_post_string = $this->m->render(
        '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
            <soap:Body>
                <ProcurementAddRs xmlns=MyNamespaceInfo">
                    <RqUID>{{RqUID}}</RqUID>
                    <PKey>{{PKey}}</PKey>
                    <ApplicationStatus>
                        <ApplicationStatusCode>{{Status}}</ApplicationStatusCode>
                    </ApplicationStatus>
                </ProcurementAddRs>
            </soap:Body>
        </soap:Envelope>', array("RqUID" =>$this->RqUID, "PKey"=>$this->PKey, "Status"=>$this->Status));
        $url = 'http://localhost/soap/gotit.php'; // in this test, it writes the response to a file. I will be sending it to the endpoint from here
        $this->curl_post_async($url, $xml_post_string);
    } // function SendAck

    function curl_post_async($url, $post_string){
        $parts=parse_url($url);

        $fp = fsockopen($parts['host'],
            isset($parts['port'])?$parts['port']:80,
            $errno, $errstr, 30);

        $out = "POST ".$parts['path']." HTTP/1.1\r\n";
        $out.= "Host: ".$parts['host']."\r\n";
        $out.= "Content-Type: text/xml\r\n";
        $out.= "Content-Length: ".strlen($post_string)."\r\n";
        $out.= "Connection: Close\r\n\r\n";
        if (isset($post_string)) $out.= $post_string;
        fwrite($fp, $out);
        fclose($fp);
    } // function curl_post_async  

    function BuildResponse() {
        $this-response = "<XML response that is built goes here>";
    }

}

【讨论】:

  • 是的,我知道,我将其标记为版主协助,因为 FoxVSky 修改了他的答案以使用与我发布的基本相同的技术
【解决方案2】:

您可以使用PHP Output Control Functions

这是我的示例用法

soapserver.php:

<?php

$data = file_get_contents('php://input');
$fp = fopen('data.txt', 'a');
fwrite($fp, json_encode($data));
fwrite($fp, "\n");

class MySoapServer {

    public function addNumbers($num1, $num2) {
        return $num1 + $num2;
    }

}

$options = array('uri' => 'http://test.local/');
$server = new SoapServer(NULL, $options);
$server->setClass('MySoapServer');

ob_end_clean();
header("Connection: close\r\n");
ignore_user_abort(true);
ob_start();
$server->handle();
$soapXml = ob_get_contents();
$size = ob_get_length();
// Flush (send) the output buffer and turn off output buffering
ob_end_clean();
ob_start();
header("Content-Length: $size");
echo $soapXml;
ob_end_flush();
// Unless both are called !
flush();
// Continue do another process after sent message
//example
sleep(10);
fwrite($fp, "Test Writing\n");
fclose($fp);

?>

soapclient.php:

<?php

// client
$options = array(
    'location' => 'http://test.local/stack/soapserver.php',
    'uri' => 'http://test.local/stack/soapserver.php'
);

$client = new SoapClient(NULL, $options);
echo $client->addNumbers(3, 5); //  8

您会立即在浏览器中看到回复 8。 10s 后,你会在文件 data.txt 中看到Test Writing

【讨论】:

  • 你能重做这个来展示如何在 SOAP 事务中做到这一点吗?我不希望你得到这个答案的赏金。
  • 如果您将使用标准 SoapServer,您可以在回显测试字符串的位置插入对其句柄方法的调用。 (php.net/manual/ru/soapserver.handle.php)
  • @DanielProtopopov,我看不到你在说什么,在这里。
  • @FoxVSky,您的解决方案在 SOAP 事务中不起作用,即使您没有展示如何使用它。
  • 我是说他的示例可以与标准 SoapServer 一起使用,以稍微不同的方式实现您的目标。
【解决方案3】:

如果您正在编写 SOAP 服务,为什么不简单地使用 SoapServerhandle() 方法呢?您不需要实际实现 TCP 握手(发送ACK)等等。我很确定这些类/方法都已为您处理好了。

缺少上下文,所以这是我最有根据的猜测。

【讨论】:

  • 您能进一步解释一下吗? handle() 方法仅将请求 XML 作为参数..
  • 我没有关注。要我解释一下handle() 方法的工作原理吗?
  • 不,我想让你解释一下你的意思。我知道 handle 方法是如何工作的,你是在暗示使用它来处理 ACK,但这并不是故意的。
  • 内容不缺,原题中的cmets对原因越来越深入。
  • 您应该编辑您的问题,并添加在 cmets 中讨论过的其他相关信息,以便人们帮助您。就像我说的那样,您不需要处理协议的细节问题。我什至不确定你会如何在 PHP 中做到这一点,除非你开始打开套接字。我的意思是,已经有一个 SOAP 服务器类可以为您处理所有这些事情。
猜你喜欢
  • 2012-10-29
  • 1970-01-01
  • 1970-01-01
  • 2023-04-10
  • 1970-01-01
  • 2021-03-15
  • 1970-01-01
  • 1970-01-01
  • 2018-04-27
相关资源
最近更新 更多