过滤器

未匹配的标注

过滤器

过滤器是 控制器动作 执行之前或之后执行的对象。例如,访问控制过滤器可在动作执行之前来控制特殊终端用户是否有权限执行动作,内容压缩过滤器可在动作执行之后发给终端用户之前压缩响应内容。

过滤器可包含预过滤(过滤逻辑在动作之前)或后过滤(过滤逻辑在动作之后),也可同时包含两者。

使用过滤器

过滤器本质上是一类特殊的 行为,所以使用过滤器和 使用行为 一样。可以在控制器类中覆盖它的 [[yii\base\Controller::behaviors()]] 方法来声明过滤器,如下所示:

public function behaviors()
{
    return [
        [
            'class' => 'yii\filters\HttpCache',
            'only' => ['index', 'view'],
            'lastModified' => function ($action, $params) {
                $q = new \yii\db\Query();
                return $q->from('user')->max('updated_at');
            },
        ],
    ];
}

控制器类的过滤器默认应用到该类的 所有 动作,你可以配置 [[yii\base\ActionFilter::only]] 属性明确指定控制器应用到哪些动作。在上述例子中,HttpCache 过滤器只应用到 indexview 动作。也可以配置 [[yii\base\ActionFilter::except]] 属性使一些动作不执行过滤器。

除了控制器外,可在 模块应用主体 中申明过滤器。申明之后,过滤器会应用到所属该模块或应用主体的 所有 控制器动作,除非像上述一样配置过滤器的 [[yii\base\ActionFilter::only]] 和 [[yii\base\ActionFilter::except]] 属性。

Note: 在模块或应用主体中申明过滤器,在[[yii\base\ActionFilter::only]] 和 [[yii\base\ActionFilter::except]] 属性中使用 路由 代替动作 ID,因为在模块或应用主体中只用动作ID并不能唯一指定到具体动作。

多过滤器

当一个动作有多个过滤器时,根据以下规则先后执行:

  • 预过滤
    • 按顺序执行应用主体中 behaviors() 列出的过滤器。
    • 按顺序执行模块中 behaviors() 列出的过滤器。
    • 按顺序执行控制器中 behaviors() 列出的过滤器。
    • 如果任意过滤器终止动作执行,后面的过滤器(包括预过滤和后过滤)不再执行。
  • 成功通过预过滤后执行动作。
  • 后过滤
    • 倒序执行控制器中 behaviors() 列出的过滤器。
    • 倒序执行模块中 behaviors() 列出的过滤器。
    • 倒序执行应用主体中 behaviors() 列出的过滤器。

创建过滤器

继承 yii\base\ActionFilter 类并覆盖 [[yii\base\ActionFilter::beforeAction()]] 或 [[yii\base\ActionFilter::afterAction()]] 方法来创建动作的过滤器,前者在动作执行之前执行,后者在动作执行之后执行。[[yii\base\ActionFilter::beforeAction()]] 返回值决定动作是否应该执行,如果为 false,之后的过滤器和动作不会继续执行。

下面的例子申明一个记录动作执行时间日志的过滤器。

namespace app\components;

use Yii;
use yii\base\ActionFilter;

class ActionTimeFilter extends ActionFilter
{
    private $_startTime;

    public function beforeAction($action)
    {
        $this->_startTime = microtime(true);
        return parent::beforeAction($action);
    }

    public function afterAction($action, $result)
    {
        $time = microtime(true) - $this->_startTime;
        Yii::debug("Action '{$action->uniqueId}' spent $time second.");
        return parent::afterAction($action, $result);
    }
}

核心过滤器

Yii 提供了一组常用过滤器,在 yii\filters 命名空间下,接下来我们简要介绍这些过滤器。

访问控制过滤器

yii\filters\AccessControl 提供基于 [[yii\filters\AccessControl::rules]] 规则的访问控制。特别是在动作执行之前,访问控制会检测所有规则并找到第一个符合上下文的变量(比如用户 IP 地址、登录状态等)的规则,来决定允许还是拒绝请求动作的执行,如果没有规则符合,访问就会被拒绝。

如下示例表示表示允许已认证用户访问 createupdate 动作,拒绝其他用户访问这两个动作。

use yii\filters\AccessControl;

public function behaviors()
{
    return [
        'access' => [
            'class' => AccessControl::class,
            'only' => ['create', 'update'],
            'rules' => [
                // 允许认证用户
                [
                    'allow' => true,
                    'roles' => ['@'],
                ],
                // 默认禁止其他用户
            ],
        ],
    ];
}

更多关于访问控制的详情请参阅 授权 一节。

认证方法过滤器

认证方法过滤器通过 HTTP 基础认证OAuth 2 来认证一个用户,认证方法过滤器类在 yii\filters\auth 命名空间下。

如下示例表示可使用 yii\filters\auth\HttpBasicAuth 来认证一个用户,它使用基于 HTTP 基础认证方法的令牌。注意为了可运行,yii\web\User::identityClass 用户身份类必须实现 [[yii\web\IdentityInterface::findIdentityByAccessToken()]] 方法。

use yii\filters\auth\HttpBasicAuth;

public function behaviors()
{
    return [
        'basicAuth' => [
            'class' => HttpBasicAuth::class,
        ],
    ];
}

认证方法过滤器通常在实现 RESTful API 中使用,更多关于访问控制的详情请参阅 RESTful 认证 一节。

内容转让过滤器

yii\filters\ContentNegotiator 支持响应内容格式处理和语言处理。通过检查 GET 参数和 Accept HTTP 头部来决定响应内容格式和语言。

在控制器或模块中使用 ContentNegotiator 作为动作过滤器,并支持 JSON 和 XML 响应格式和英语(美国)和德语,如下示例:

use yii\filters\ContentNegotiator;
use yii\web\Response;

public function behaviors()
{
    return [
        [
            'class' => ContentNegotiator::class,
            // 在一个控制器里
            'only' => ['view', 'index'],
            // 如果是在模块中,则使用以下 id 代表用户动作
            // 'only' => ['user/view', 'user/index']
            'formats' => [
                'application/json' => Response::FORMAT_JSON,
                'application/xml' => Response::FORMAT_XML,
            ],
            'languages' => [
                'en-US',
                'de',
            ],
        ],
    ];
}

应用主体生命周期 过程中检测响应格式和语言简单很多,因此 ContentNegotiator 设计可被 引导启动组件 调用的过滤器。如下例所示可以将它配置在 应用主体配置

use yii\filters\ContentNegotiator;
use yii\web\Response;

[
    'bootstrap' => [
        [
            'class' => ContentNegotiator::class,
            'formats' => [
                'application/json' => Response::FORMAT_JSON,
                'application/xml' => Response::FORMAT_XML,
            ],
            'languages' => [
                'en-US',
                'de',
            ],
        ],
    ],
];

Info: 如果请求中没有检测到内容格式和语言,使用 formatslanguages 首个配置项。

Http 缓存过滤器

yii\filters\HttpCache 利用 Last-ModifiedEtag HTTP 头实现客户端缓存。例如:

use yii\filters\HttpCache;

public function behaviors()
{
    return [
        [
            'class' => HttpCache::class,
            'only' => ['index'],
            'lastModified' => function ($action, $params) {
                $q = new \yii\db\Query();
                return $q->from('user')->max('updated_at');
            },
            // 生成 响应中的“ETag”报头字段 种子字符串的PHP回调函数
            //'etagSeed' => function ($action, $params) {
            //    return // 构造 ETag seed
            //}
        ],
    ];
}

更多关于使用它的详情,请参阅 HTTP 缓存 一节。

页面缓存过滤器

yii\filters\PageCache 实现服务器端整个页面的缓存。如下示例所示,PageCache 应用在 index 动作,缓存整个页面 60 秒或 post 表的记录数发生变化。它也会根据不同应用语言保存不同的页面版本。

use yii\filters\PageCache;
use yii\caching\DbDependency;

public function behaviors()
{
    return [
        'pageCache' => [
            'class' => PageCache::class,
            'only' => ['index'],
            'duration' => 60,
            'dependency' => [
                'class' => DbDependency::class,
                'sql' => 'SELECT COUNT(*) FROM post',
            ],
            'variations' => [
                \Yii::$app->language,
            ]
        ],
    ];
}

更多关于使用它的详情,请参阅 页面缓存 一节。

限流过滤器

yii\filters\RateLimiter 根据 漏桶算法 来实现一个速率限制算法。你可以使用 RateLimiter,将它作为一个行为,附加到控制器或模块,如下所示:

public function behaviors()
{
    return [
        'rateLimiter' => [
            'class' => \yii\filters\RateLimiter::class,
        ],
    ];
}

当用户超过速率限制时,RateLimiter 将抛出一个 [[TooManyRequestsHttpException]] 异常。

Note:如果 user 组件的 identityClass 没有设置(相当于未登录授权的游客)或者没有实现 yii\filters\RateLimitInterface,RateLimiter 将不生效。

速率限制,主要用在实现 RESTful APIs,就像老生常谈的接口限流,更多关于该过滤器详情请参阅 速率限制 一节。

HTTP 请求方法过滤器

yii\filters\VerbFilter 检查请求动作的 HTTP 请求方式是否允许执行,如果不允许,会抛出 HTTP 405异常。如下示例,VerbFilter 指定 CRUD 动作所允许的请求方式。

use yii\filters\VerbFilter;

public function behaviors()
{
    return [
        'verbs' => [
            'class' => VerbFilter::class,
            'actions' => [
                'index'  => ['get'],
                'view'   => ['get'],
                'create' => ['get', 'post'],
                'update' => ['get', 'put', 'post'],
                'delete' => ['post', 'delete'],
            ],
        ],
    ];
}

CORS 过滤器

CORS(跨域资源共享) 机制允许一个网页的许多资源(例如字体、JavaScript等)可以通过其他域名访问获取。特别是 JavaScript 的 AJAX 调用,可使用 XMLHttpRequest 机制,由于同源安全策略该跨域请求会被网页浏览器禁止。CORS 定义浏览器和服务器交互时哪些跨域请求允许和禁止。

yii\filters\Cors CORS 过滤器应在 授权认证过滤器 之前定义,以保证 CORS 头部被发送。

use yii\filters\Cors;
use yii\helpers\ArrayHelper;

public function behaviors()
{
    return ArrayHelper::merge([
        [
            'class' => Cors::class,
        ],
    ], parent::behaviors());
}

如果要将 CORS 过滤器添加到你的 API 中的 [[yii\rest\ActiveController]] 类,还要检查 REST Controllers 中的部分。

CROS 过滤器可以通过 [[yii\filters\Cors::$cors]] 属性进行调整。

  • cors['Origin']:定义允许来源的数组,可为 ['*'](任何用户)或 ['http://www.myserver.net', 'http://www.myotherserver.com']。 默认为 ['*']
  • cors['Access-Control-Request-Method']:允许动作数组如 ['GET', 'OPTIONS', 'HEAD']。默认为 ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']
  • cors['Access-Control-Request-Headers']:允许请求头部数组,可为 ['*'] 所有类型头部 或 ['X-Request-With'] 指定类型头部。默认为 ['*']
  • cors['Access-Control-Allow-Credentials']:定义当前请求是否使用证书,可为 truefalsenull(不设置)。默认为 null
  • cors['Access-Control-Max-Age']: 定义请求的有效时间,默认为 86400

详细示例,如下:

use yii\filters\Cors;
use yii\helpers\ArrayHelper;

public function behaviors()
{
    return ArrayHelper::merge([
        [
            'class' => Cors::class,
            'cors' => [
                // 允许来源
                'Origin' => ['http://www.myserver.com', 'https://www.myserver.com'],
                // 只允许 POST 和 PUT 方法
                'Access-Control-Request-Method' => ['POST', 'PUT'],
                // 允许请求头部 'X-Wsse'
                'Access-Control-Request-Headers' => ['X-Wsse'],
                // 允许使用证书 (cookies, authorization headers等) 向浏览器公开
                'Access-Control-Allow-Credentials' => true,
                // Allow OPTIONS caching
                'Access-Control-Max-Age' => 3600,
                // 允许 X-Pagination-Current-Page 头文件暴露给浏览器
                'Access-Control-Expose-Headers' => ['X-Pagination-Current-Page'],
            ],
        ],
    ], parent::behaviors());
}

可以覆盖默认参数为每个动作调整 CORS 头部。例如,为 login 动作增加 Access-Control-Allow-Credentials 参数如下所示:

use yii\filters\Cors;
use yii\helpers\ArrayHelper;

public function behaviors()
{
    return ArrayHelper::merge([
        [
            'class' => Cors::class,
            'cors' => [
                'Origin' => ['http://www.myserver.net'],
                'Access-Control-Request-Method' => ['GET', 'HEAD', 'OPTIONS'],
            ],
            'actions' => [
                'login' => [
                    'Access-Control-Allow-Credentials' => true,
                ]
            ]
        ],
    ], parent::behaviors());
}

HOST 过滤器

Yii 从 2.0.11 版本起,提供 yii\filters\HostControl 对请求的主机名提供简单的控制。这个过滤器,提供了针对 [“主机头”攻击] 的保护,且只允许对指定的主机名执行操作。

可以在应用主体配置,示例如下:

return [
     'as hostControl' => [
         'class' => 'yii\filters\HostControl',
         'allowedHosts' => [
             'example.com',
             '*.example.com',
         ],
     ],
     // ...
];

也可以在控制器中使用,具体如下:

use yii\web\Controller;
use yii\filters\HostControl;

class SiteController extends Controller
{
    public function behaviors()
    {
        return [
            'hostControl' => [
                'class' => HostControl::class,
                'allowedHosts' => [
                    'example.com',
                    '*.example.com',
                ],
            ],
        ];
    }

    // ...
}

Note:最佳限制允许主机名的方法是,使用 web 服务器的“虚拟主机”配置。只有在此配置不可用或受到损害时,才应使用此筛选器。

AJAX 过滤器

Yii 从 2.0.13 版本起,提供 yii\filters\AjaxFilter,允许仅对 ajax 请求限制访问。示例如下:

public function behaviors()
{
    return [
        [
            'class' => 'yii\filters\AjaxFilter',
            'only' => ['index']//'errorMessage' = 'Request must be XMLHttpRequest.'
        ],
    ];
}

💖喜欢本文档的,欢迎点赞、收藏、留言或转发,谢谢支持!
作者邮箱:zhuzixian520@126.com,github地址:github.com/zhuzixian520

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
zhuzixian520
讨论数量: 0
发起讨论 只看当前版本


暂无话题~