设置跨域

yii2的跨域设置是非常便捷的,只需要设置 behaviors 中的 corsFilter 即可。

  1. <?php
  2. namespace api\controllers;
  3. use common\web\CompositeAuth;
  4. use yii\filters\Cors;
  5. use yii\filters\auth\HttpBearerAuth;
  6. use yii\rest\Controller;
  7. /**
  8. * 基础校验控制
  9. *
  10. * @author vogin
  11. *
  12. */
  13. class BasicController extends Controller
  14. {
  15. public function behaviors ()
  16. {
  17. $behaviors = parent::behaviors();
  18. unset($behaviors['authenticator']);
  19. // 跨域设置
  20. $behaviors['corsFilter'] = [
  21. 'class' => Cors::className()
  22. ];
  23. // 校验 Authorization bearer
  24. $behaviors['authenticator'] = [
  25. 'class' => CompositeAuth::className(),
  26. 'authMethods' => [
  27. HttpBearerAuth::className()
  28. ]
  29. ];
  30. return $behaviors;
  31. }
  32. }

问题

但是,有时候,怎么配置都达不到效果出现报错

Access to XMLHttpRequest at ‘http://api.xxx.com‘ from origin ‘http://localhost:8080‘ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

目前发现的原因有两个。

问题1

权鉴在跨域之前被设置,比如这样。先是 $behaviors[‘authenticator’],再者 $behaviors[‘corsFilter’],这样会导致先鉴权再跨域,所以需要将设置提前,也就是 “设置跨域” 那样设置

  1. <?php
  2. public function behaviors ()
  3. {
  4. $behaviors = parent::behaviors();
  5. unset($behaviors['authenticator']);
  6. // 校验 Authorization bearer
  7. $behaviors['authenticator'] = [
  8. 'class' => CompositeAuth::className(),
  9. 'authMethods' => [
  10. HttpBearerAuth::className()
  11. ]
  12. ];
  13. // 跨域设置
  14. $behaviors['corsFilter'] = [
  15. 'class' => Cors::className()
  16. ];
  17. return $behaviors;
  18. }

问题2

路由问题。 由于并非继承 ActiveController,而是 yii\rest\Controll,所以路由都是自行指定的。 跨域检测是由 OPTIONS 方法去探测的,所以可能是此方法没有着落。 比如下面的方式,我并未指定 OPTIONS 对应的路由,所以其会报错

  1. <?php
  2. 'urlManager' => [
  3. 'enablePrettyUrl' => true,
  4. 'showScriptName' => false,
  5. 'rules' => [
  6. // 获取所有标签
  7. 'GET v1/tags' => 'v1/tag/all',
  8. // 获取单个标签
  9. 'GET v1/tags/<id:\d+>' => 'v1/tag/one',
  10. // 添加单个标签
  11. 'POST v1/tags' => 'v1/tag/add',
  12. // 编辑单个标签
  13. 'PUT v1/tags' => 'v1/tag/edit',
  14. // 删除单个标签
  15. 'DELETE v1/tags' => 'v1/tag/del',
  16. ]
  17. ],

解决此问题需要指定 OPTIONS,并且添加对应的方法

路由

  1. <?php
  2. 'urlManager' => [
  3. 'enablePrettyUrl' => true,
  4. 'showScriptName' => false,
  5. 'rules' => [
  6. // options 检测
  7. 'OPTIONS v1/tags' => 'v1/tag/options',
  8. ]
  9. ],

公共控制器中,增加 OPTIONS,有几个模块,加几次

  1. <?php
  2. /**
  3. * 基础校验控制
  4. *
  5. * @author vogin
  6. *
  7. */
  8. class BasicController extends Controller
  9. {
  10. public function actions ()
  11. {
  12. return [
  13. 'options' => [
  14. 'class' => 'yii\rest\OptionsAction'
  15. ]
  16. ];
  17. }
  18. // ......
  19. }

其他情况

那就直接在 index.php 的最上面把 php 跨域的代码写上

  1. <?php
  2. header('Content-Type: text/html;charset=utf-8');
  3. header('Access-Control-Allow-Origin:*'); // *代表允许任何网址请求
  4. header('Access-Control-Allow-Methods:POST,GET,OPTIONS,DELETE'); // 允许请求的类型
  5. header('Access-Control-Allow-Credentials: true'); // 设置是否允许发送 cookies
  6. header('Access-Control-Allow-Headers: Content-Type,Content-Length,Accept-Encoding,X-Requested-with, Origin'); // 设置允许自定义请求头的字段

如何排查

断点

最好的排查方式就是打断点,OPTIONS 也是一个完整的请求,在idea上使用 xdebug 断点能快速调试