【问题标题】:Use CakePHP Http Client with Magento2 rest API search criteria将 CakePHP Http Client 与 Magento2 REST API 搜索条件一起使用
【发布时间】:2017-08-21 23:24:53
【问题描述】:

我正在尝试向本地 Magento2 REST API 发送 GET 请求,以在特定时间后获取所有订单。我正在关注http://devdocs.magento.com/guides/v2.1/howdoi/webapi/search-criteria.html#simple-search-using-a-timestamp。我正在使用 CakePHP 3.4 的 Http 客户端 (https://book.cakephp.org/3.0/en/core-libraries/httpclient.html),并且已经使用 Oauth1 成功地与 Magento 集成,并且对于像 http://www.magento.dev.com/rest/V1/stockItems/:productSku 这样的更简单的 GET 请求没有任何问题。通过搜索条件是一个问题。响应始终是401 Invalid Signature

使用 Postman,我可以得到对 http://www.magento.dev.com/rest/V1/orders?searchCriteria[filter_groups][0][filters][0][field]=created_at&searchCriteria[filter_groups][0][filters][0][value]=2016-07-01 00:00:00&searchCriteria[filter_groups][0][filters][0][condition_type]=gt 的有效回复

这是我目前所拥有的/我发送请求的方式:

在 Model/Table/OrdersTable.php 中:

public function importNewOrders(\App\Model\Entity\OauthIntegration $integrationDetails)
    {
       $this->OauthIntegrations = TableRegistry::get('OauthIntegrations');
       $this->Orders = TableRegistry::get('Orders');
       $timeCutOff = '2015-01-01 00:00:00';
       $search = [
           'searchCriteria' => [
                'filterGroups' => [
                    0 => [
                        'filters' => [
                            0 => [
                                'field' => 'created_at',
                                'value' => $timeCutOff,
                                'condition_type' => 'gt'
                            ]
                        ]
                    ]
                ]
           ]
        ];
//           'searchCriteria[filter_groups][0][filters][0][field]' => 'created_at',
//           'searchCriteria[filter_groups][0][filters][0][value]' => $timeCutOff, 
//           'searchCriteria[filter_groups][0][filters][0][condition_type]' => 'gt'

       $action = '/V1/orders';

       $type = "GET";
       $response = $this->OauthIntegrations->sendRequest(
               $integrationDetails, 
               $action, 
               $type,
               '',
               $search);

       Log::write('debug', $response->body());
       return $response;
    }

在 Model\Table\OauthIntegrationsTable.php 中:

public function sendRequest(\App\Model\Entity\OauthIntegration $integrationDetails, 
            string $action, string $method = "GET", string $data = '', array $search = null)
    {

        $http = new Client([
            'auth' => [
                'type' => 'oauth',
                'consumerKey' => $integrationDetails->oauth_consumer_key,
                'consumerSecret' => $integrationDetails->oauth_consumer_secret,
                'token' => $integrationDetails->oauth_token,
                'tokenSecret' => $integrationDetails->oauth_token_secret
              ]
          ]);
        $url = $integrationDetails->store_base_url . 'rest' . $action;
        if ($method == 'GET'){
            if (!isset($search)){
                $search = [];
            }
            $response = $http->get($url, $search, []);

        } else if ($method == 'POST'){

            $response = $http->post($url, $data, [
              'type' => 'json',

            ]);

        } else if($method == 'PUT'){
            $response = $http->put($url, $data, [
              'type' => 'json',
            ]);
        }
        Log::write('debug', 'url: ' . $url . ' and status code: ' . $response->getStatusCode());
        return $response;
    }

这是错误(我希望)是无效签名响应的原因:

2017-03-28 10:07:01 Notice: Notice (8): Array to string conversion in [/var/www/cakephp/html/beacon/vendor/cakephp/cakephp/src/Http/Client/Auth/Oauth.php, line 315]
Trace:
Cake\Error\BaseErrorHandler::handleError() - CORE/src/Error/BaseErrorHandler.php, line 153
Cake\Http\Client\Auth\Oauth::_normalizedParams() - CORE/src/Http/Client/Auth/Oauth.php, line 315
Cake\Http\Client\Auth\Oauth::baseString() - CORE/src/Http/Client/Auth/Oauth.php, line 246
Cake\Http\Client\Auth\Oauth::_hmacSha1() - CORE/src/Http/Client/Auth/Oauth.php, line 143
Cake\Http\Client\Auth\Oauth::authentication() - CORE/src/Http/Client/Auth/Oauth.php, line 61
Cake\Http\Client::_addAuthentication() - CORE/src/Http/Client.php, line 501
Cake\Http\Client::_createRequest() - CORE/src/Http/Client.php, line 448
Cake\Http\Client::_doRequest() - CORE/src/Http/Client.php, line 341
Cake\Http\Client::get() - CORE/src/Http/Client.php, line 211
App\Model\Table\OauthIntegrationsTable::sendRequest() - APP/Model/Table/OauthIntegrationsTable.php, line 134
App\Model\Table\OrdersTable::importNewOrders() - APP/Model/Table/OrdersTable.php, line 672
App\Shell\MagentoShell::main() - APP/Shell/MagentoShell.php, line 36
Cake\Console\Shell::runCommand() - CORE/src/Console/Shell.php, line 472
Cake\Console\ShellDispatcher::_dispatch() - CORE/src/Console/ShellDispatcher.php, line 227
Cake\Console\ShellDispatcher::dispatch() - CORE/src/Console/ShellDispatcher.php, line 182
Cake\Console\ShellDispatcher::run() - CORE/src/Console/ShellDispatcher.php, line 128
[main] - ROOT/bin/cake.php, line 33

发生错误的 Http\Client\Oauth.php 中的代码:

        $pairs = [];
        foreach ($args as $k => $val) {
            if (is_array($val)) {
                sort($val, SORT_STRING);
                Log::write('debug', 'about to go through foreach($val as $nestedVal)');
                foreach ($val as $nestedVal) {
                    Log::write('debug', $nestedVal);
                    $pairs[] = "$k=$nestedVal"; // <<< HERE
                }
            } else {
                $pairs[] = "$k=$val";
            }
        }

从上面调试结果:

2017-03-28 10:07:01 Debug: about to go through foreach($val as $nestedVal)
2017-03-28 10:07:01 Debug: Array
(
    [0] => Array
        (
            [filters] => Array
                (
                    [0] => Array
                        (
                            [field] => created_at
                            [value] => 2015-01-01 00:00:00
                            [condition_type] => gt
                        )

                )

        )

)

总之,是否可以使用 Cake 的 Http Client 将多维数组传递给 get 请求中的第二个参数?

// Is it possible to replace ['q' => 'widget'] with a multi-dimensional array??
$response = $http->get('http://example.com/search', ['q' => 'widget']);

如果不是,使用 Cake 的 Http 客户端发送 GET 请求到:http://www.magento.dev.com/rest/V1/orders?searchCriteria[filter_groups][0][filters][0][field]=created_at&amp;searchCriteria[filter_groups][0][filters][0][value]=2016-07-01 00:00:00&amp;searchCriteria[filter_groups][0][filters][0][condition_type]=gt 的最佳方式是什么?

提前致谢!!!

【问题讨论】:

    标签: rest magento cakephp oauth cakephp-3.x


    【解决方案1】:

    可能的错误

    这可能被认为是一个可能的错误。我不认为the OAuth specs 考虑到 URL 中的这种 PHP 样式括号内容,因此对参数进行排序/编码仅限于平面 key=value 集合,即键是

    searchCriteria[filter_groups][0][filters][0][field]
    

    值是

    created_at
    

    但是,CakePHP OAuth 适配器将请求查询字符串解析为可能深度嵌套的数组结构,然后会失败,因为它不处理这种情况。

    我建议你report this as a possible bug。可能会出现进一步的问题,因为编码似乎是在排序之前应用的,在 CakePHP 实现中,additonal 参数编码是在排序之后应用的(虽然这实际上可能很好,我不确定) .

    尝试使用自定义 OAuth 适配器作为解决方法

    在修复/增强此问题之前,您可以使用自定义 OAuth 适配器来“正确”处理事情(无论在这种情况下意味着什么)。这是一个快速而肮脏的示例(适用于 Magento API)。

    创建src/Http/Client/Auth/AppOAuth.php

    <?php
    namespace App\Http\Client\Auth;
    
    use Cake\Http\Client\Auth\Oauth;
    
    class AppOAuth extends Oauth
    {
        protected function _normalizedParams($request, $oauthValues)
        {
            $query = parse_url($request->url(), PHP_URL_QUERY);
            parse_str($query, $queryArgs);
    
            $post = [];
            $body = $request->body();
            if (is_string($body) &&
                $request->getHeaderLine('content-type') === 'application/x-www-form-urlencoded'
            ) {
                parse_str($body, $post);
            }
            if (is_array($body)) {
                $post = $body;
            }
    
            $args = array_merge($queryArgs, $oauthValues, $post);
            $query = http_build_query($args);
    
            $args = [];
            foreach (explode('&', $query) as $value) {
                $pair = explode('=', $value, 2);
                $args[] =
                    rawurlencode(rawurldecode($pair[0])) .
                    '=' .
                    rawurlencode(rawurldecode($pair[1]));
            }
            usort($args, 'strcmp');
    
            return implode('&', $args);
        }
    }
    

    比较 \Cake\Http\Client\Auth\Oauth::_normalizedParams()

    通过在type 选项中为您的客户端实例指定类名来使用它:

    'type' => 'AppOAuth',
    

    ps

    不应该是 filter_groups 而不是 filterGroups 在您的 $search 数组中吗?

    【讨论】:

    • 已提交错误:github.com/cakephp/cakephp/issues/10458。像上面那样创建自定义 Oauth 适配器解决了数组到字符串通知,但仍然有 401 无效签名。正在传递 url,方括号被编码为 %5D 和 %5B。我尝试在包含 AppOAuth.php 中的括号的键上使用 urldecode,但在将请求发送为在 cakephp/src//Http/Client.php 保护函数 _doRequest($method, $url, $data, $options) 对括号进行了编码。该编码会导致401吗?感谢您的帮助!
    • @BSounder AFAICT 应该没问题,它只是百分比编码,对 URL 完全有效。我可以再看看你的具体情况,我试过不同的。
    • 我也是这么想的,特别是因为我看到的所有内容都指向 url 中的方括号违反 URI 标准(stackoverflow.com/questions/40568/… / ietf.org/rfc/rfc3986.txt),因此它们应该被编码
    • @BSounder 看起来日期值中的空格是这种情况下的问题,在生成签名时它应该以%252B结尾,但是它只以%2B的形式出现,这似乎源于从_normalizedParams() 中的请求对象读取URL 时,它以+ 的形式出现,而不是%2B。不知道谁是正确的,我没有深入研究 OAuth RFC,但我可以想象 Magento 要求这个是正确的。
    • @BSounder 更新了代码示例...现在可以工作了,但这里的事情真的失控了,我不知道它是否偶然工作,或者它是否真的解码/编码正确的东西以正确的方式放置 - 我讨厌阅读 RFC,现在已经是凌晨 2 点了 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-16
    • 1970-01-01
    • 1970-01-01
    • 2016-03-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多