如何在 Yii 框架中使用 HTTP PING?

How can I use HTTP PING in Yii framework?

我正在尝试使用 HTTP PING 请求,我的操作作为直接 GET 请求的测试,但是当我在操作中测试它时,Yii returns 400 Bad Request.

<a href="<?= Url::toRoute(['shopping/ping', 'id' => (string)$item->productId, 
  'category' => (string)$item->primaryCategory->categoryId]) ?>">test</a>
<a href="<?= $item->viewItemURL ?>" ping="<?= Url::toRoute(['shopping/ping', 'id' => (string)$item->productId, 
  'category' => (string)$item->primaryCategory->categoryId]) ?>">  

这是浏览器发送的请求:

POST /aa/web/index.php?r=shopping%2Fping&id=102712943&category=122930 HTTP/1.1
Host: localhost:81
Connection: keep-alive
Content-Length: 4
Origin: http://localhost:81
Ping-From: http://localhost:81/aa/web/index.php?r=shopping%2Fsearch
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.76 Safari/537.36
Content-Type: text/ping
Cache-Control: max-age=0
Ping-To: http://www.ebay.com/itm/Oster-TSSTTVVG01-4-Slice-Toaster-Oven-Black-Free-Shipping-New-/111355397466?pt=LH_DefaultDomain_0
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
Cookie: ...chocolate chip...

PING

这是回复:

HTTP/1.1 400 Bad Request
Date: Fri, 20 Mar 2015 04:46:37 GMT
Server: Apache/2.2.29 (Unix) mod_ssl/2.2.29 OpenSSL/1.0.1k DAV/2 PHP/5.6.6
X-Powered-By: PHP/5.6.6
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Set-Cookie: ...
Connection: close
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8

我的猜测是因为内容类型或者因为它是一个带有 URL 参数的 POST 请求并且正文没有路由。

这里是 curl 请求。

$ curl "http://localhost:81/aa/web/index.php?r=shopping"%"2Fping&id=102712943&category=122930" -H "Origin: http://localhost:81" -H "Accept-Encoding: gzip, deflate" -H "Accept-Language: en-US,en;q=0.8" -H "User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.76 Safari/537.36" -H "Content-Type: text/ping" -H "Accept: */*" -H "Ping-From: http://localhost:81/aa/web/index.php?r=shopping"%"2Fsearch" -H "Cookie: _ga=GA1.1.1670952969.1415577943; _sp_id.1fff=1d72e39980ea4f9f.1418093158.62.1423448353.1420663798; _csrf=62bd0371770906a42eb6da654fb4345ba51adb9272a26d1dc8ce6d28908f6610a"%"3A2"%"3A"%"7Bi"%"3A0"%"3Bs"%"3A5"%"3A"%"22_csrf"%"22"%"3Bi"%"3A1"%"3Bs"%"3A32"%"3A"%"22Dq2_r1KD_6GR-rHBEm3b4g6foEKxFL5q"%"22"%"3B"%"7D; PHPSESSID=j4unbucemcp4pj1h87qimld596; _identity=df1823bd01881d804ff9655d79364c898d2ad574cc769f3a53fb104dde8e3f88a"%"3A2"%"3A"%"7Bi"%"3A0"%"3Bs"%"3A9"%"3A"%"22_identity"%"22"%"3Bi"%"3A1"%"3Bs"%"3A28"%"3A"%"22"%"5B"%"22100"%"22"%"2C"%"22test100key"%"22"%"2C2592000"%"5D"%"22"%"3B"%"7D" -H "Connection: keep-alive" -H "Cache-Control: max-age=0" -H "Ping-To: http://www.ebay.com/itm/Oster-TSSTTVVG01-4-Slice-Toaster-Oven-Black-Free-Shipping-New-/111355397466?pt=LH_DefaultDomain_0" --data-binary "PING"                 
...
<h1>Bad Request (#400)</h1>

我回到我的应用程序并单击调试器栏,然后加载失败的请求。这是它给出的错误和堆栈跟踪。

exception 'yii\web\BadRequestHttpException' with message 'Unable to verify your data submission.' in /cygdrive/c/Users/Chloe/workspace/AffiliateArbitrage/vendor/yiisoft/yii2/web/Controller.php:110
Stack trace:
#0 /cygdrive/c/Users/Chloe/workspace/AffiliateArbitrage/vendor/yiisoft/yii2/base/Controller.php(149): yii\web\Controller->beforeAction(Object(yii\base\InlineAction))
#1 /cygdrive/c/Users/Chloe/workspace/AffiliateArbitrage/vendor/yiisoft/yii2/base/Module.php(455): yii\base\Controller->runAction('ping', Array)
#2 /cygdrive/c/Users/Chloe/workspace/AffiliateArbitrage/vendor/yiisoft/yii2/web/Application.php(83): yii\base\Module->runAction('shopping/ping', Array)
#3 /cygdrive/c/Users/Chloe/workspace/AffiliateArbitrage/vendor/yiisoft/yii2/base/Application.php(375): yii\web\Application->handleRequest(Object(yii\web\Request))
#4 /cygdrive/c/Users/Chloe/workspace/AffiliateArbitrage/web/index.php(12): yii\base\Application->run()
#5 {main}

如何让这个请求生效?

这是我对问题的理解。首先,您收到的错误与 csrf 验证直接相关。验证失败,因此永远不会处理请求。当您单击 link 时,您正在发出 GET 请求,因此不会调用 csrf 验证。

ping 属性通过 POST 方法向服务器提交请求。因为它是一个 POST 方法,Yii 期望在请求的 body 中找到一个有效的 csrf 令牌,但没有找到,因为没有提交表单。

后备方法是检查 headers 以查看 header HTTP_X_CSRF_TOKEN 中是否存在有效的 csrf 令牌,因此您可能可以写一些 javascript 拦截 ping 请求并添加所需的 header,从现有 cookie 中获取令牌。不过我的 javascript 写的不够好,所以我无法测试它!

另一种选择是编写一个新控制器 class,覆盖默认控制器的 beforeAction() 方法以删除 csrf 验证部分,并让您的特定 ShoppingController 扩展该新控制器。

希望对您有所帮助!

是的,这是因为 CSRF,这是一个解决方案:

  public function beforeAction($action) {
    // Yii::trace($action); // this will prevent debug bar from showing!
    if ($action->id == 'ping') $this->enableCsrfValidation = false;
    return parent::beforeAction($action);
  }

我标记了另一个答案以表彰该建议,尽管没有示例。