我正在尝试使用cURL编写连接器以连接到REST API。
用户必须执行的第一步是使用createSession()
创建会话。这将使用用户名和密码向API发送POST
调用。 API将使用sessionId,cookie值和一对自定义标头进行响应。
会话仅在每个有效请求后3分钟有效。如果我在会话过期后发出请求,我将收到http error code 401
,表示用户未经授权发出请求,因为sessionId无效或超时。
我没有要求用户再次手动登录,而是希望在收到错误401时通过调用createSession()
方法自动重新连接。我之所以需要这样做,是因为sessionId保存在客户端中cookie,因此客户端不知道会话是否过期或活动。我的代码将尝试使用保存在cookie中的sessionId来调用方法,这些方法是活动的或已过期的。
只要会话仍处于活动状态,每次发出请求时,API都会将会话续订3分钟。我唯一需要重新连接的时间是用户没有提出3分钟的请求。
我遇到的问题是当我尝试重新组合时,我进入一个无限循环,我无法弄清楚如何阻止它。
这是我的代码
<?php namespace API;
/**
* ICWS API
*
* @package ICWS
*/
class ICWS {
private $_myAppName = 'ICWS API connector';
private $_authenticationType = 'Basic'; //Not used yet
private $_languageID = 'en-US';
private $_protocol = 'http';
private $_sessionIdKey = 'sessionId';
private $_interactionIdKey = 'interactionIdKey';
private $_maxLoginAttempts = 3;
private $_loginAttempts = 0;
private $_debug = false;
//No need to edit beyond this line
private $_isSubscribledToQueue = false;
private $_alternateHostList = array();
private $_interactionId = 0;
private $_queueType = 1;
private $_userID;
private $_password;
private $_workstation;
private $_queueName;
private $_cainfo;
private $_baseURL;
private $_csrfToken;
private $_sessionId;
private $_ININ_ICWS_CSRF_Token;
private $_Location;
private $_subscriptionId;
private $_curlHeader;
private $_requestFile;
public function __construct($config)
{
//Make sure cURL is enabled on the server
if(!is_callable('curl_init')){
throw new ApiException('cURL is disabled on this server. Before making API calls cURL extension must be enabled.');
}
//Make sure all required config are set
if( !isset($config['host']) || empty($config['host'])
|| !isset($config['port']) || empty($config['port'])
|| !isset($config['userID']) || empty($config['userID'])
|| !isset($config['password']) || empty($config['password'])
|| !isset($config['workstation']) || empty($config['workstation'])
){
throw new ApiException('Host, Port, userID, password, workstation are required!');
}
$this->_userID = $config['userID'];
$this->_password = $config['password'];
$this->_workstation = $config['workstation'];
//override the default queueType
if( isset($config['queueType']) && !empty($config['queueType']) ){
$this->_queueType = $config['queueType'];
}
//override the default queueName
if( isset($config['queueName']) && !empty($config['queueName']) ){
$this->_queueName = $config['queueName'];
}
//override the default appName
if( isset($config['appName']) && !empty($config['appName']) ){
$this->_myAppName = $config['appName'];
}
//override the default session Key
if( isset($config['sessionKey']) && !empty($config['sessionKey']) ){
$this->_sessionKey = $config['sessionKey'];
}
//override the default protocol
if( isset($config['isSecured']) && $config['isSecured'] == true){
if(!isset($config['cainfo']) || empty($config['cainfo'])){
throw new ApiException('To enable SSL you must provide CA Info file (.cert)');
} else {
$this->_protocol = 'https';
$this->cainfo = $config['cainfo'];
}
}
//override the default server Language
if( isset($config['languageID']) && !empty($config['languageID']) ){
$this->_languageID = $config['languageID'];
}
//override the default debug mode
if( isset($config['debug']) && !empty($config['debug']) ){
$this->_debug = $config['debug'];
}
//override the default authentication type
if( isset($config['authenticationType']) && !empty($config['authenticationType']) ){
$this->_authenticationType = $config['authenticationType'];
}
//set the sessionId if it already exists
if( isset( $_COOKIE[$this->_sessionIdKey] ) && !empty( $_COOKIE[$this->_sessionIdKey] )){
$this->_sessionId = $_COOKIE[$this->_sessionIdKey];
}
//set the _interactionIdKey if it already exists
if( isset( $_COOKIE[$this->_interactionIdKey] ) && !empty( $_COOKIE[$this->_interactionIdKey] )){
$this->_interactionId = $this->_bigint($_COOKIE[$this->_interactionIdKey]);
}
if(isset($_COOKIE['ININ-ICWS-CSRF-Token']) && !empty($_COOKIE['ININ-ICWS-CSRF-Token'])){
$this->_ININ_ICWS_CSRF_Token = $_COOKIE['ININ-ICWS-CSRF-Token'];
}
$this->_baseURL = $this->_protocol . '://' . $config['host'] . ':' . $config['port'] . '/icws/';
$this->_subscriptionId = $this->_userID;
}
/**
* Authentication the user and generated a sessionId
*
* @param string $userID
* @param string $password
* @param boolean $forceNewSession
* @catch exception
* @return void
*/
public function createSession($forceNewSession = false){
if( !empty($this->_sessionId) && ! $forceNewSession ){
return;
}
if($forceNewSession){
$this->destroySession();
}
$this->_requestFile = 'connection';
$type = 'urn:inin.com:connection:icAuthConnectionRequestSettings';
$data = array('__type' => $type,
'applicationName' => $this->_myAppName,
'userID' => $this->_userID,
'password' => $this->_password);
$this->_curlHeader = array('Accept-Language: ' . $this->_languageID,
'Content-Type: application/json');
$httpCode = 0;
try {
$data = $this->_processRequest('POST', 'connection', $data, $httpCode, false);
if($this->_debug){
new showVar($data, false, 'HTTP Code: ' . $httpCode);
}
$this->_csrfToken = $data['csrfToken'];
$this->_sessionId = $data['sessionId'];
$this->_alternateHostList = $data['alternateHostList'];
if(!empty($this->_sessionId)){
setCookie($this->_sessionIdKey, $this->_sessionId);
$this->_loginAttempts = 0;
}
} catch (\Exception $e){
$this->_displayError($e);
}
}
/**
* Destroy the IC session
*
* @return void
*/
public function destroySession(){
//destroy the sessionId
$this->_sessionId = NULL;
$this->_destroy($this->_sessionIdKey);
//destroy the sessionId
$this->_interactionIdKey = 0;
$this->_destroy($this->_interactionIdKey);
//destroy the CSRF-Token
$this->_ININ_ICWS_CSRF_Token = NULL;
$this->_destroy('ININ-ICWS-CSRF-Token');
}
/**
* Calls any Method after a session is created
*
* @param string $method GET/POST/PUT
* @param string $uri
* @param array $data
* @catch exception
* @return array or false
*/
private function _sendRequest($method, $uri, $data = false, &$httpCode = 0){
if( !$this->_sessionId ){
return false;
}
$uri = $this->_sessionId . '/' . $uri;
$return = false;
//,'Cookie: ' . $this->_ININ_ICWS_Cookie
$this->_curlHeader = array('ININ-ICWS-CSRF-Token: ' . $this->_ININ_ICWS_CSRF_Token,
'ININ-ICWS-Session-ID: ' . $this->_sessionId,
'Content-Type: application/json');
try {
$return = $this->_processRequest($method, $uri, $data, $httpCode);
} catch (\Exception $e){
$this->_displayError($e);
} finally {
return $return;
}
}
/**
* Handle the cURL call to the API
*
* @throws ApiException
* @param string $method GET/POST/PUT
* @param string $uri
* @param array $data
* @param array &$httpCode
* @return array
*/
private function _processRequest($method, $uri, $data = false, &$httpCode = NULL, $allowReconnect = true)
{
$ch = curl_init();
$url = $this->_baseURL . $uri;
if(
($method == 'POST' || $method == 'PUT')
&& $data
){
$jsonString = json_encode($data);
curl_setopt( $ch, CURLOPT_POSTFIELDS, $jsonString );
}
if($method == 'POST'){
curl_setopt($ch, CURLOPT_POST, true);
} elseif( $method == 'PUT'){
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
} else {
if ($data){
$url = sprintf("%s?%s", $url, http_build_query($data));
}
}
//set the URL
curl_setopt($ch, CURLOPT_URL, $url);
//disable the use of cached connection
curl_setopt($ch, CURLOPT_FRESH_CONNECT, true);
//return the respond from the API
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
//return the HEADER respond from the API
curl_setopt($ch, CURLOPT_HEADER, true);
//add custom headers
if(!empty($this->_curlHeader)){
curl_setopt($ch, CURLOPT_HTTPHEADER, $this->_curlHeader);
}
//add the cookie value
$cookiesFile = 'icwsCookies';
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookiesFile); // write
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookiesFile); // read
//enable SSL
if( $this->_protocol == 'https' ){
curl_setopt($ch, CURLOPT_CAINFO, $this->_cainfo);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, true);
}
//send the request to the API
$respond = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
//throw cURL exception
if($respond === false){
$errorNo = curl_errno($ch);
$errorMessage = curl_error($ch);
throw new ApiException($errorMessage, $errorNo);
}
list($header, $body) = explode("\r\n\r\n", $respond, 2);
if($uri == 'connection'){
$this->_handleReceivedHeaders($header);
}
//if user gets unauthorized error attempt to login as long as the attempt are under 3
if($httpCode == 401 && $allowReconnect){
if( $this->_loginAttempts > $this->_maxLoginAttempts){
throw new ApiException('All Attempts to create a session have been used! Please check your credentials and try again');
} else {
$this->_reconnect($method, $uri, $data);
}
}
//convert respond to an array
return json_decode($body, true);
}
/**
* Reconnect to the Api and generate a new sessionId
*
* @return boolean
*/
private function _reconnect($method, $uri, $data){
$this->createSession(true);
$httpCode = 0;
$this->_processRequest($method, $uri, $data, $httpCode);
if($httpCode == 200 || $httpCode == 201){
return true;
}
return false;
}
/**
* Get the cookie HTTP headers and set them as cookie
*
* @param array $httpRespond
* @return void
*/
private function _handleReceivedHeaders($httpRespond){
$header = $this->_http_parse_headers($httpRespond);
//set the ININ-ICWS-CSRF-Token value
if( isset($header['ININ-ICWS-CSRF-Token']) ){
$this->_ININ_ICWS_CSRF_Token = $header['ININ-ICWS-CSRF-Token'];
setCookie('ININ-ICWS-CSRF-Token', $this->_ININ_ICWS_CSRF_Token);
}
}
/**
* Checks if the API return an error
*
* @param array $result
* @return boolean
*/
private function _hasAPIError($result){
if( isset($result['errorId']) && !empty($result['errorId'])
&& isset($result['message']) && !empty($result['message'])
){
return true;
}
return false;
}
/**
* Displays the exception details
*
* @param ApiException $e
*/
private function _displayError(ApiException $e){
echo 'Error Number: ' . $e->getCode() . "<br>";
echo $e->getMessage() . "<br><br>";
}
/**
* convert cURL header into an array
*
* @param string $raw_headers
* @return array
*/
private function _http_parse_headers($raw_headers)
{
$headers = array();
$key = '';
foreach(explode("\n", $raw_headers) as $i => $h)
{
$h = explode(':', $h, 2);
if (isset($h[1])){
if (!isset($headers[$h[0]])){
$headers[$h[0]] = trim($h[1]);
} elseif (is_array($headers[$h[0]])){
$headers[$h[0]] = array_merge($headers[$h[0]], array(trim($h[1]))); // [+]
} else {
$headers[$h[0]] = array_merge(array($headers[$h[0]]), array(trim($h[1]))); // [+]
}
$key = $h[0];
} else {
if (substr($h[0], 0, 1) == "\t"){
$headers[$key] .= "\r\n\t".trim($h[0]);
} elseif (!$key){
$headers[0] = trim($h[0]);trim($h[0]);
}
}
}
return $headers;
}
/**
* return a valid numeric value
*
* @param string $val
* @return big integer
*/
private function _bigint($val){
$val = filter_var($val, FILTER_SANITIZE_NUMBER_INT);
if(empty($val)){
$val = 0;
}
return $val;
}
/**
* Destroy a cookie
* @return void
*/
private function _destroy($name){
setcookie($name, null);
unset($_COOKIE[$name]);
}
}
?>
下面的这个片段是我尝试重新连接API的地方。由于某种原因导致循环。
if($httpCode == 401 && $allowReconnect){
if( $this->_loginAttempts > $this->_maxLoginAttempts){
throw new ApiException('All Attempts to create a session have been used! Please check your credentials and try again');
} else {
$this->_reconnect($method, $uri, $data);
}
}
以下是我的代码摘要。
通过$this->createSession(true);
创建会话
然后在不同的时间调用多个_processRequests()
方法。如果_processRequests()
方法返回401,则调用$this->createSession(true);
,直到它返回代码201或200或$this->createSession(true);
被调用超过3次,然后我将需要完全。问题是即使$this->createSession(true);
返回代码200或201它仍然循环并且它不会停止
循环的原因是_processRequests()
在达到错误401时调用自身无限。它无法识别第二个调用返回201。
答案 0 :(得分:0)
在_reconnect
方法中:
$this->createSession(true);
在createSession
方法中:
$data = $this->_processRequest('POST', 'connection', $data, $httpCode, false);
在_processRequest
方法中:
//if user gets unauthorized error attempt to login as long as the attempt are under 3
if($httpCode == 401 && $allowReconnect){
if( $this->_loginAttempts > $this->_maxLoginAttempts){
throw new ApiException('All Attempts to create a session have been used! Please check your credentials and try again');
} else {
$this->_reconnect($method, $uri, $data);
}
}
我的猜测是你遇到了未经授权的错误,因为你永远不会在代码中的任何地方递增$this->_loginAttempts
,所以它永远不会超过$this->_maxLoginAttempts
,所以代码会再次调用_reconnect
mehtod,导致它进入无限循环。