【问题标题】:PHP CURL Get request with uri containing dollar sign returns 400 Bad RequestPHP CURL 获取包含美元符号的 uri 的请求返回 400 Bad Request
【发布时间】: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-controlpostman-token(channeladvisor rest api 不需要)。问题似乎与生成请求 uri 的方式有关。查看 php urlencode 结果的方式与邮递员的方式。在 php 中,/ 正在被编码,不知道如何告诉 php 像邮递员那样生成 url。
  • getpostman.com/docs/requests EncodeURIComponent 将它添加到 url 栏中和我相信的键值之间是有区别的。请阅读文档

标签: php rest curl


【解决方案1】:

我已经像这样(现在)解决了它,它对我来说工作正常。让我知道是否有更好/正确的方法来做到这一点。

/**
 * 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 . self::formatUri($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);

    // 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 format query string
 *
 * @param $queryString
 * @return string
 */
private function formatUri($queryString)
{
    return str_replace(
        ['$', ' '],
        ['%24', '%20'],
        $queryString
    );
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-19
    • 2011-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-29
    • 1970-01-01
    相关资源
    最近更新 更多