我正在尝试在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。
几周前,我已经与iress.com联系,他们打电话给我以确认我已获得访问该API的权限,并告诉我他们会保持联系-尚未进行跟进电话我想回到这个项目上。
我确实知道Smith
的姓氏与我的查询相匹配。
我没有Python经验,因此错误响应对我没有帮助。我所做的 hail mary 尝试比我发布的要多,但是我已经浪费了时间。我不知道第三个参数是否应嵌套在<value>
,<param>
,<struct>
,<string>
,<array>
,{{1 }}或其他所有内容。
如果有人对如何为请求准备XML查询有任何建议,我将运行它们并提供反馈。
我也很高兴收到有关类设计,安全性问题以及完全不同的php方法(使<xml>
返回一些有用数据的php方法)的建议。
按照@ThW的要求,这是xml尝试及其各自的错误响应的集合:https://pastebin.com/dYtwXWxz
答案 0 :(得分:1)
在黑暗中拍摄,因为我无法直接测试API ...
也许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而不是)?
XML查询需要被视为一个字符串,因此请确保在GUI客户端发出请求时,将其结束编码/包装在CDATA标记中。取决于您选择的客户,它可能会为您完成,只需确保已完成
答案 1 :(得分:1)
不幸的是,在亲自向IRESS支持并继续调查之后(尽管编写了手册,表达了如何与API集成),API的唯一许可用法是“用东芝扫描仪上传文档”。 / p>
要获得访问权限,IRESS和公司的法律团队将需要在上述编辑的URL中草拟新的许可证文件。这不太可能是一个快速的尝试。
答案 2 :(得分:1)
事实证明,该错误不是我的php代码(此后我进一步开发该错误以更好地处理响应数据),而是我正在发送的xml查询。
代替使用文档中的建议:
<SearchQuick><s>last_name:Smith</s></SearchQuick>
使用以下等效的有效表达式:
<SearchByField field="last_name" op="equal"><s>Smith</s></SearchByField>