【发布时间】:2016-01-18 12:56:06
【问题描述】:
我正在尝试使用 PHP 中的 ChannelAdvisor REST API 来加载自上次同步以来发生变化的库存水平列表。这个过程记录在这里:http://developers.channeladvisor.com/rest/#946
根据文档,我在 Post Man(chrome rest 客户端)中进行了测试,以确保它按预期工作并且确实如此:
由于测试成功,我开始编写 PHP 脚本以以相同的方式与 REST API 进行交互,但它并不完全正常工作。我得到以下输出:
错误请求 - HTTP 错误 400。请求格式错误。
到目前为止,这是我的脚本(我正在使用 Laravel 4,但与此无关)。问题在于curlGET 方法:
<?php namespace Latheesan\ThirdParty\ChannelAdvisorREST;
class ChannelAdvisorREST {
/**
* ChannelAdvisor base uri
*/
const BASE_URL = 'https://api.channeladvisor.com';
/**
* ChannelAdvisor config data
*/
private $config;
/**
* Class constructor
*/
public function __construct()
{
$this->config = \Config::get('channeladvisor');
}
/**
* Method to load stock updates since last sync.
*
* @param $accountId
* @param $lastSync
* @return array
*/
public function getStockUpdates($accountId, $lastSync)
{
// Anticipate errors
try
{
// Init
$stockUpdates = [];
// Query channel advisor
$stockUpdateResults = self::curlGET($accountId, '/v1/Products?$filter=QuantityUpdateDateUtc gt '. $lastSync);
// TODO: parse $stockUpdateResults into $stockUpdates
// Success
return $this->successResponse($stockUpdateResults);
}
catch (\Exception $ex)
{
// Error response
return $this->errorResponse('Failed to load stock updates - '. $ex->getMessage());
}
}
/**
* Generic method to output error responses.
*
* @param string $message
* @return array
*/
private function errorResponse($message = '')
{
// Error
return [
'IsError' => true,
'ErrorMsg' => $message,
'Data' => ''
];
}
/**
* Generic method to output success responses.
*
* @param $data
* @return array
*/
private function successResponse($data)
{
// Success
return [
'IsError' => false,
'ErrorMsg' => '',
'Data' => $data
];
}
/**
* Method to get access token from rest server.
*
* @param $accountId
* @return string
* @throws \Exception
*/
private function getAccessToken($accountId)
{
// Define cache key
$cache_key = 'CA_REST_ACCESS_TOKEN.'. $accountId;
// Check if there is a cached version of access token
if (\Cache::has($cache_key))
return \Cache::get($cache_key);
// Anticipate errors
try
{
// Call rest api server
$response = self::curlPOST('/oauth2/token', [
'client_id' => $this->config['api_app_id'],
'grant_type' => 'soap',
'scope' => 'inventory',
'developer_key' => $this->config['api_developer_key'],
'password' => $this->config['api_password'],
'account_id' => $accountId
]);
// Check if there was an error
if (isset($response->Message))
throw new \Exception($response->Message);
if (isset($response->error))
throw new \Exception($response->error);
// Check if there was an invalid response
if (!isset($response->access_token) || !isset($response->expires_in))
throw new \Exception('Invalid response - '. json_encode($response));
// Cache server response
\Cache::add($cache_key, $response->access_token, floor($response->expires_in / 60));
// Success
return $response->access_token;
}
catch (\Exception $ex)
{
// Rethrow error
throw new \Exception('Failed to load rest api access token - '. $ex->getMessage());
}
}
/**
* Method to generate a HTTP POST request
*
* @param $endpoint
* @param array $fields
* @return string
* @throws \Exception
*/
private function curlPOST($endpoint, $fields = array())
{
// Open connection
$ch = curl_init();
// Set the url, number of POST vars, POST data
curl_setopt($ch, CURLOPT_USERPWD, $this->config['api_app_id'] .':'. $this->config['api_shared_secret']);
curl_setopt($ch, CURLOPT_URL, self::BASE_URL . $endpoint);
curl_setopt($ch, CURLOPT_POST, count($fields));
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($fields, '', '&'));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/x-www-form-urlencoded'
));
curl_setopt($ch, CURLOPT_VERBOSE, true);
$verbose = fopen('php://temp', 'w+');
curl_setopt($ch, CURLOPT_STDERR, $verbose);
// Execute post request
$result = curl_exec($ch);
// Debug error
if ($result === FALSE) {
$curlError = 'Error #'. curl_errno($ch) .':'. htmlspecialchars(curl_error($ch));
rewind($verbose);
$verboseLog = stream_get_contents($verbose);
$curlError .= "\r\nDebug Info: ". htmlspecialchars($verboseLog);
curl_close($ch);
throw new \Exception($curlError);
}
@fclose($verbose);
// Close connection
curl_close($ch);
// Finished
return json_decode($result);
}
/**
* Method to generate HTTP GET request
*
* @param $accountId
* @param $queryString
* @return string
* @throws \Exception
*/
private function curlGET($accountId, $queryString)
{
// Open connection
$ch = curl_init();
// Set the url, query string & access token
curl_setopt($ch, CURLOPT_URL, self::BASE_URL . $queryString);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Authorization: Bearer '. self::getAccessToken($accountId)
));
curl_setopt($ch, CURLOPT_VERBOSE, true);
$verbose = fopen('php://temp', 'w+');
curl_setopt($ch, CURLOPT_STDERR, $verbose);
// Execute post request
$result = curl_exec($ch);
// TESTING
var_dump($result); exit;
// TESTING
// Debug error
if ($result === FALSE) {
$curlError = 'Error #'. curl_errno($ch) .':'. htmlspecialchars(curl_error($ch));
rewind($verbose);
$verboseLog = stream_get_contents($verbose);
$curlError .= "\r\nDebug Info: ". htmlspecialchars($verboseLog);
curl_close($ch);
throw new \Exception($curlError);
}
@fclose($verbose);
// Close connection
curl_close($ch);
// Finished
return json_decode($result);
}
}
上面是一个外观类,所以我是这样使用的:
echo '<pre>';
print_r(ChannelAdvisorREST::getStockUpdates
(
'xxx-xxx-xxx', // accountId
'2016-01-18T11:50:03.000Z' // lastSync utc date & time
));
echo '</pre>';
知道为什么它在 Postman REST 客户端中有效,但在我的 PHP 中失败了吗?我以为我正在做完全相同的步骤。我也试过urlencode查询字符串;但这也不起作用。
【问题讨论】:
-
Postman 通过单击代码符号(在 PHPCurl 中)为您生成请求。它会产生什么?
-
哦,哇,这很方便。不知道它可以做到这一点。我得到以下回复:pastebin.com/r8KKQQbS(看起来查询字符串是 urlencoded)。
-
当我使用
curl_setopt($ch, CURLOPT_URL, self::BASE_URL . urlencode($queryString));时,uri php 生成如下所示:https://api.channeladvisor.com%2Fv1%2FProducts%3F%24filter%3DQuantityUpdateDateUtc+gt+2016-01-18T11%3A50%3A03.000Z这与生成的邮递员不同:https://api.channeladvisor.com/v1/Products?%24filter=QuantityUpdateDateUtc%20gt%202016-01-18T12%3A20%3A03.000- 那么邮递员如何部分 urlencoding? :s -
不怕,表头没问题。 Postman 只是添加了这两个额外的标题:
cache-control和postman-token(channeladvisor rest api 不需要)。问题似乎与生成请求 uri 的方式有关。查看 php urlencode 结果的方式与邮递员的方式。在 php 中,/正在被编码,不知道如何告诉 php 像邮递员那样生成 url。 -
getpostman.com/docs/requests
EncodeURIComponent将它添加到 url 栏中和我相信的键值之间是有区别的。请阅读文档