代码之家  ›  专栏  ›  技术社区  ›  mickmackusa Tom Green

如何在对xplan api的curl请求中构造edai.search方法的第三个参数?

  •  7
  • mickmackusa Tom Green  · 技术社区  · 6 年前

    我正试图在一个API上运行一个搜索,该API要求我的查询数据被设计为XML嵌套在一个XML请求中。我将发布我的整个类和方法调用(我将其发送给IRESS技术支持),以便对其进行全面检查,并且在任何人都可以访问同一API的情况下,他们可以立即为自己复制该问题。

    class XMLCurler
    {
        private $username = '[redacted]';
        private $password = '[redacted]';
        private $url = 'https://[redacted].xplan.iress.com.au/RPC2/';
        public $ch;     // the curl handle
        public $token;  
        public $results;
    
        public function __construct() {
            if ($this->connect()) {
                if ($this->login()) {
                    echo "<div class=\"success\">Successful Connection & Login. Token: {$this->token}</div>";
                }
            }
        }
    
        public function __destruct() {
            if ($this->ch) {
                $this->disconnect();
            }
        }
    
        public function connect() {
            if (!$this->ch = curl_init($this->url)) { // generate curl handle
                echo "<div class=\"error\">CURL Error While Connecting (check url)";
                return false;
            }
            return true;
        }
    
        public function disconnect() {
            curl_close($this->ch);
        }
    
        public function processResponse($response) {
            if (!$response) {
                echo "<div class=\"error\">CURL Error While Attempting to Login - No XML token string<br><b>" , curl_error($this->ch) , "</b></div>";
                return false;
            }
            $decoded = xmlrpc_decode($response);
            if (is_array($decoded) && xmlrpc_is_fault($decoded)) {
                echo "<div class=\"error\">Error Response: {$decoded['faultString']} ({$decoded['faultCode']})</div>";
                return false;
            }
            return $decoded;    
        }
    
        public function login() {
            $postfields = xmlrpc_encode_request('edai.Login', array($this->username, $this->password));  // package as xml
            curl_setopt($this->ch, CURLOPT_HTTPHEADER, array('Content-Type: text/xml'));
            curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($this->ch, CURLOPT_POSTFIELDS, $postfields);
            curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 0);      // not advised, I need to find out how to avoid this
            curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, 0);      // not advised, I need to find out how to avoid this
    
            if (!$token = $this->processResponse(curl_exec($this->ch))) {
                return false;
            }
            if (!preg_match("~^[\w+]{20}$~", $token)) {
                echo "<div class=\"error\">Invalid/Unexpected Token Generated<br><b>$token</b>";
                return false;
            }
            $this->token = $token;  // cache the valid token
            return true;
        }
    
        public function listChildren($path) {
            $method = "edai.ListChildren";
            $request = xmlrpc_encode_request($method, array($this->token, $path));
    
            echo "<div class=\"notice\">XMLRPC Encoded Request (for $method): <pre>" , htmlentities($request) , "</pre></div>";
    
            curl_setopt($this->ch, CURLOPT_POSTFIELDS, $request);       
            if (!$results = $this->processResponse(curl_exec($this->ch))) {
                return false;
            }
            $this->results = $results;  // cache the valid results
            return true;
        }
    
    
        public function search($basepath, $queryxml) {
            $method = "edai.Search";
    
            /** Desperate/Manual xml construction ...
             * $xml = new DOMDocument("1.0", "utf-8");
             * $xml->appendChild($methodCall = $xml->createElement("methodCall"));
             * $methodCall->appendChild($methodName = $xml->createElement("methodName"));
             * $methodCall->appendChild($params = $xml->createElement("params"));
    
             * $params->appendChild($param1 = $xml->createElement("param"));
             * $param1->appendChild($value1 = $xml->createElement("value"));
             * $value1->appendChild($string1 = $xml->createElement("string"));
    
             * $params->appendChild($param2 = $xml->createElement("param"));
             * $param2->appendChild($value2 = $xml->createElement("value"));
             * $value2->appendChild($string2 = $xml->createElement("string"));
    
             * $params->appendChild($param3 = $xml->createElement("param"));
             * $param3->appendChild($value3 = $xml->createElement("value"));
             * $value3->appendChild($string3 = $xml->createElement("string"));
             * $string3->appendChild($EntitySearch = $xml->createElement("EntitySearch"));
             * $EntitySearch->appendChild($SearchResult1 = $xml->createElement("SearchResult"));
             * $SearchResult1->setAttribute("field", "first_name");
    
             * $EntitySearch->appendChild($SearchResult2 = $xml->createElement("SearchResult"));
             * $SearchResult2->setAttribute('field', "last_name");
    
             * $EntitySearch->appendChild($SearchQuick = $xml->createElement("SearchQuick"));
             * $SearchQuick->appendChild($s = $xml->createElement("s"));
    
             * $xpath = new DOMXPath($xml);
             * $result1 = $xpath->query("//methodName");
             * $result1->item(0)->nodeValue = $method;
    
             * $result2 = $xpath->query("//params/param[1]/value/string");
             * $result2->item(0)->nodeValue = $this->token;
             * $result3 = $xpath->query("//params/param[2]/value/string");
             * $result3->item(0)->nodeValue = "entitymgr/client";
    
             * $result4 = $xpath->query("//SearchQuick/s");
             * $result4->item(0)->nodeValue = "last_name:Smith";
    
             * $xml->formatOutput = true;
             * $request = $xml->saveXML();
             */
    
             /** Desperately attempted passing array ...
              * $queryarray = array(
              *     "EntitySearch" => array(
              *         array(
              *             "SearchResult" => array(
              *                 "@attr" => array(
              *             "field" => "first_name"
              *                 )
              *             )
              *         ),
              *             array(
              *             "SearchResult" => array(
              *                 "@attr" => array(
              *                     "field" => "last_name"
              *                 )
              *             )
              *         ),
              *         array(
              *             "SearchQuick" => array(
              *                 "s" => "last_name:Smith"
              *             )
              *         )
              *     )
              * );
              */
    
            $request = xmlrpc_encode_request($method, array($this->token, $basepath, $queryxml));  // this mutates the nested $queryxml string
                // Causes:
                    //Error Response: UNKNOWN(CORBA.UNKNOWN(omniORB.UNKNOWN_PythonException, CORBA.COMPLETED_MAYBE)) (-32505)
            //$request = html_entity_decode($request);  // repair encoded entities
            //$request = preg_replace('~(?:>\K\s+)|(?:\s+(?=<))~', '', $request);  // strip every whitespace character between tags (hack)
                // Causes:
                    // Error Response: ExpatError(syntax error: line 1, column 0 (byte 0)) (-32505)
            echo "<div class=\"notice\">XMLRPC Encoded Request (for $method): <pre>" , htmlentities($request) , "</pre></div>";
    
            curl_setopt($this->ch, CURLOPT_POSTFIELDS, $request);
            if (!$results = $this->processResponse(curl_exec($this->ch))) {
                return false;
            }
            $this->results = $results;  // cache the valid results
            return true;
        }
    }
    

    下面是我打电话的方式。 edai.ListChildren 工作是因为我不需要发送任何XML数据。 edai.Search 不起作用,因为我无法在XML请求中正确准备XML查询。

    $XC = new XMLCurler();
    
    /* edai.ListChildren works as desired/expected */
    $path = "/entitymgr/client";
    if ($XC->listChildren($path)) {
        echo "<div>List of Children Successful.<pre>";
        var_export($XC->results);
        echo "</pre></div>";
    }
    
    /* edai.Search does not work */
    $basepath = "entitymgr/client";
    $queryxml = <<<XML
    <EntitySearch>
     <SearchResult field="first_name"/>
     <SearchResult field="last_name"/>
     <SearchQuick><s>last_name:Smith</s></SearchQuick>
    </EntitySearch>
    XML;
    if ($XC->search($basepath, $queryxml)) {
        echo "<div>Search Successful.<pre>";
        var_export($XC->results);
        echo "</pre></div>";
    }
    

    这是 the attempted request and error message .

    这是 the relevant portion of the manual I was provided (XPLAN XML-RPC EXTERNAL DATA ACCESS INTERFACE 7 May 2013) 以下内容:

    几周前我联系了iress.com,他们打电话给我,粗略地确认我有权访问API,并告诉我他们会保持联系——后续电话还没有发生,我想继续这个项目的工作。

    我知道有个姓 Smith 以匹配我的查询。

    我没有Python经验,所以错误响应对我没有帮助。我做得更多了 万岁玛丽 尝试的次数比我张贴的要多,但我厌倦了浪费时间。我不知道第三个参数是否要嵌套在 <value> ,请 <param> ,请 <struct> ,请 <string> ,请 <array> ,请 <xml> 或者别的什么。

    如果有人对如何为请求准备XML查询有任何建议,我将运行它们并提供反馈。

    我也很高兴收到关于类设计、安全性问题和完全不同的PHP方法的建议,以获得 edai.搜索 返回一些有用的数据。


    按照@thw的要求,这里是XML尝试及其各自的错误响应的集合: https://pastebin.com/dYtwXWxz

    3 回复  |  直到 6 年前
        1
  •  1
  •   jstur    6 年前

    也许xmlrpc_encode_request调用可以使用命名参数:

    $params = [
        'context' => $this->token,
        'basepath' => $basepath, // try with a leading slash, too, in spite of the docs listing it w/o one
        'queryxml' => $queryxml,
    ];
    $request = xmlrpc_encode_request($method, $params);  // this mutates the nested $queryxml string
    

    SoapUI Postman Insomnia

    我怀疑您会在半小时内收到一个有效的请求,并且可以从中向后工作来调试代码/重写代码。如果我可以访问API,我会为您做这件事。

    • 编码有什么区别吗(应该是utf8而不是?

        2
  •  1
  •   sirobertson    6 年前

    不幸的是,在亲自与IRESS支持部门交谈并继续调查之后(尽管编写了一份说明如何与API集成的手册),API的唯一许可用途是“使用东芝扫描仪上载文档”。

        3
  •  1
  •   mickmackusa Tom Green    6 年前

    <SearchQuick><s>last_name:Smith</s></SearchQuick>
    

    <SearchByField field="last_name" op="equal"><s>Smith</s></SearchByField>