蓝色主旋律 2021-04-06 09:29:28 4153次浏览 0条回复 1 2 0

设置跨域

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

<?php
namespace api\controllers;
use common\web\CompositeAuth;
use yii\filters\Cors;
use yii\filters\auth\HttpBearerAuth;
use yii\rest\Controller;
/**
 * 基础校验控制
 *
 * @author vogin
 *        
 */
class BasicController extends Controller
{
    public function behaviors ()
    {
        $behaviors = parent::behaviors();
        unset($behaviors['authenticator']);
        
        // 跨域设置
        $behaviors['corsFilter'] = [
            'class' => Cors::className()
        ];
        
        // 校验 Authorization bearer
        $behaviors['authenticator'] = [
            'class' => CompositeAuth::className(),
            'authMethods' => [
                HttpBearerAuth::className()
            
            ]
        ];
        
        return $behaviors;
    }
}

问题

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

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'],这样会导致先鉴权再跨域,所以需要将设置提前,也就是 "设置跨域" 那样设置

<?php
public function behaviors ()
{
  $behaviors = parent::behaviors();
  unset($behaviors['authenticator']);
  // 校验 Authorization bearer
  $behaviors['authenticator'] = [
    'class' => CompositeAuth::className(),
    'authMethods' => [
      HttpBearerAuth::className()
    ]
  ];
  // 跨域设置
  $behaviors['corsFilter'] = [
    'class' => Cors::className()
  ];
  return $behaviors;
}
问题2

路由问题。

由于并非继承 ActiveController,而是 yii\rest\Controller,所以路由都是自行指定的。 跨域检测是由 OPTIONS 方法去探测的,所以可能是此方法没有着落。

比如下面的方式,我并未指定 OPTIONS 对应的路由,所以其会报错

<?php
'urlManager' => [
  'enablePrettyUrl' => true,
  'showScriptName' => false,
  'rules' => [
    // 获取所有标签
    'GET v1/tags' => 'v1/tag/all',
    // 获取单个标签
    'GET v1/tags/<id:\d+>' => 'v1/tag/one',
    // 添加单个标签
    'POST v1/tags' => 'v1/tag/add',
    // 编辑单个标签
    'PUT v1/tags' => 'v1/tag/edit',
    // 删除单个标签
    'DELETE v1/tags' => 'v1/tag/del',
  ]
],

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

路由

<?php
'urlManager' => [
  'enablePrettyUrl' => true,
  'showScriptName' => false,
  'rules' => [
    // options 检测
    'OPTIONS v1/tags' => 'v1/tag/options',
  ]
],

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

<?php
/**
 * 基础校验控制
 *
 * @author vogin
 *        
 */
class BasicController extends Controller
{
    public function actions ()
    {
        return [
            'options' => [
                'class' => 'yii\rest\OptionsAction'
            ]
        ];
    }
    // ......
}
其他方案

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

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

如何排查

断点

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

觉得很赞
    没有找到数据。
您需要登录后才可以回复。登录 | 立即注册