Yii Filter Introduction
A filter is a piece of code that can be configured to execute before or after a controller action. For example, access control filters will be executed to ensure that the user is authenticated before performing the requested action; performance filters can be used to measure the time it takes for the controller to execute.
An action can have multiple filters. Filters are executed in the order they appear in the filter list. Filters can prevent actions and other subsequent filters from executing.
There are two ways to write filters:
No matter what kind of filter, you must override the controller's public function filters() method in the controller to set which filter will act on which action.
Method based filters
Writing a method-based filter requires three steps:
Write actions in the controller;
Write the filter function in the controller. The function name must be prefixed with filter, such as: function filterAccessControl();
Rewrite the filters() method of the parent class CController to define the relationship between filters and actions;
Example:
<?php class UserController extends CController{ ** * 第一步:创建动作 */ function actionAdd(){ echo "actionAdd"; } /** * 第二步:创建基于方法的过滤器 */ public function filterAddFilter($filterChain) { echo "基于方法的过滤器UserController.filterAdd<br>"; $filterChain->run(); } /** * 第三步:重写父类CController的filters()方法,定义过滤器与动作的关系 * @see CController::filters() */ public function filters(){ return array( //定义过滤器与动作的关联关系 'addFilter + add', // array( // 'application.filters.TestFilter', // ), ); } }
Custom filter class
To customize the filter class, you need to write a separate filter class, inherit the CFilter class, and override some methods under the CFilter class. You can take a look at the code of the CFilter class. There is not much code in this class and it is still easy to understand.
Custom filter example:
<?php class TestFilter extends CFilter{ /** * Performs the pre-action filtering. * @param CFilterChain $filterChain the filter chain that the filter is on. * @return boolean whether the filtering process should continue and the action * should be executed. */ protected function preFilter($filterChain) { echo "--->TestFilter.preFilter.<br>"; return true; } /** * Performs the post-action filtering. * @param CFilterChain $filterChain the filter chain that the filter is on. */ protected function postFilter($filterChain) { echo "--->TestFilter.postFilter.<br>"; } }
Register the binding relationship between the custom filter and the action in the controller:
/** * 第三步:重写父类CController的filters()方法,定义过滤器与动作的关系 * @see CController::filters() */ ublic function filters(){ return array( //定义过滤器与动作的关联关系 'addFilter + add', array( 'application.filters.TestFilter', ), );
I customized a filter: TestFilter, which inherits the CFilter class and overrides the two main methods of the CFilter class: preFilter (pre-controller, runs before the action is executed) and postFilter (post-controller, runs after the action is executed) ).
Execution sequence of the two controllers
Suppose I bind the custom filter class written above to the actionAdd. Then, the custom filter inherits two methods from the parent class CFilter: preFilter and postFilter, and the execution order between it and the bound actionAdd is What kind of thing?
After testing, the execution order is: CFilter::preFilter--------->UserController::actionAdd--------->CFilter::postFilter.
That is to say, filtering operations can be performed before and after the action is executed.
So how does it say at the beginning of the article that "Filters can prevent the execution of actions and other subsequent filters"?
You will know after reading the official comments of CFilter::preFilter:
@return boolean whether the filtering process should continue and the action should be executed.
CFilter::preFilter function returns by default
true; that is, subsequent actions and post-filters are executed by default. If in a custom filter class, override the CFilter::preFilter method and return
false; you can prevent subsequent actions and filters from executing!
Use filters
A filter is essentially a special type of behavior, so using a filter is the same as using a behavior. Filters can be declared in the controller class by overriding its yiibaseController::behaviors() method as follows:
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 过滤器只应用到index和view动作。 也可以配置yii\base\ActionFilter::except属性使一些动作不执行过滤器。
除了控制器外,可在 模块或应用主体 中申明过滤器。 申明之后,过滤器会应用到所属该模块或应用主体的 所有 控制器动作, 除非像上述一样配置过滤器的 yii\base\ActionFilter::only 和 yii\base\ActionFilter::except 属性。
补充: 在模块或应用主体中申明过滤器,在yii\base\ActionFilter::only 和 yii\base\ActionFilter::except 属性中使用路由 代替动作ID, 因为在模块或应用主体中只用动作ID并不能唯一指定到具体动作。.
当一个动作有多个过滤器时,根据以下规则先后执行:
预过滤
后过滤
创建过滤器
继承 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::trace("Action '{$action->uniqueId}' spent $time second."); return parent::afterAction($action, $result); } }
核心过滤器
Yii提供了一组常用过滤器,在yii\filters命名空间下,接下来我们简要介绍这些过滤器。
1.yii\filters\AccessControl
AccessControl提供基于yii\filters\AccessControl::rules规则的访问控制。 特别是在动作执行之前,访问控制会检测所有规则并找到第一个符合上下文的变量(比如用户IP地址、登录状态等等)的规则, 来决定允许还是拒绝请求动作的执行,如果没有规则符合,访问就会被拒绝。
如下示例表示表示允许已认证用户访问create 和 update 动作,拒绝其他用户访问这两个动作。
use yii\filters\AccessControl; public function behaviors() { return [ 'access' => [ 'class' => AccessControl::className(), 'only' => ['create', 'update'], 'rules' => [ // 允许认证用户 [ 'allow' => true, 'roles' => ['@'], ], // 默认禁止其他用户 ], ], ]; }
2.认证方法过滤器
认证方法过滤器通过HTTP Basic Auth或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::className(), ], ]; }
认证方法过滤器通常在实现RESTful API中使用。
3.yii\filters\ContentNegotiator
ContentNegotiator支持响应内容格式处理和语言处理。 通过检查 GET 参数和 Accept HTTP头部来决定响应内容格式和语言。
如下示例,配置ContentNegotiator支持JSON和XML响应格式和英语(美国)和德语。
use yii\filters\ContentNegotiator; use yii\web\Response; public function behaviors() { return [ [ 'class' => ContentNegotiator::className(), '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::className(), 'formats' => [ 'application/json' => Response::FORMAT_JSON, 'application/xml' => Response::FORMAT_XML, ], 'languages' => [ 'en-US', 'de', ], ], ], ];
补充: 如果请求中没有检测到内容格式和语言,使用formats和languages第一个配置项。
4.yii\filters\HttpCache
HttpCache利用Last-Modified 和 Etag HTTP头实现客户端缓存。例如:
use yii\filters\HttpCache; public function behaviors() { return [ [ 'class' => HttpCache::className(), 'only' => ['index'], 'lastModified' => function ($action, $params) { $q = new \yii\db\Query(); return $q->from('user')->max('updated_at'); }, ], ]; }
5.yii\filters\PageCache
PageCache实现服务器端整个页面的缓存。如下示例所示,PageCache应用在index动作, 缓存整个页面60秒或post表的记录数发生变化。它也会根据不同应用语言保存不同的页面版本。
use yii\filters\PageCache; use yii\caching\DbDependency; public function behaviors() { return [ 'pageCache' => [ 'class' => PageCache::className(), 'only' => ['index'], 'duration' => 60, 'dependency' => [ 'class' => DbDependency::className(), 'sql' => 'SELECT COUNT(*) FROM post', ], 'variations' => [ \Yii::$app->language, ] ], ]; }
6.yii\filters\RateLimiter
RateLimiter 根据 漏桶算法 来实现速率限制。
7.yii\filters\VerbFilter
VerbFilter检查请求动作的HTTP请求方式是否允许执行,如果不允许,会抛出HTTP 405异常。 如下示例,VerbFilter指定CRUD动作所允许的请求方式。
use yii\filters\VerbFilter; public function behaviors() { return [ 'verbs' => [ 'class' => VerbFilter::className(), 'actions' => [ 'index' => ['get'], 'view' => ['get'], 'create' => ['get', 'post'], 'update' => ['get', 'put', 'post'], 'delete' => ['post', 'delete'], ], ], ]; }
8.yii\filters\Cors
跨域资源共享 CORS 机制允许一个网页的许多资源(例如字体、JavaScript等) 这些资源可以通过其他域名访问获取。 特别是JavaScript's AJAX 调用可使用 XMLHttpRequest 机制,由于同源安全策略该跨域请求会被网页浏览器禁止. CORS定义浏览器和服务器交互时哪些跨域请求允许和禁止。
yii\filters\Cors 应在 授权 / 认证 过滤器之前定义,以保证CORS头部被发送。
use yii\filters\Cors; use yii\helpers\ArrayHelper; public function behaviors() { return ArrayHelper::merge([ [ 'class' => Cors::className(), ], ], parent::behaviors()); }
Cors 可转为使用 cors 属性。
例如,允许来源为 http://www.myserver.net 和方式为 GET, HEAD 和 OPTIONS 的CORS如下:
use yii\filters\Cors; use yii\helpers\ArrayHelper; public function behaviors() { return ArrayHelper::merge([ [ 'class' => Cors::className(), 'cors' => [ 'Origin' => ['http://www.myserver.net'], 'Access-Control-Request-Method' => ['GET', 'HEAD', 'OPTIONS'], ], ], ], parent::behaviors()); }
可以覆盖默认参数为每个动作调整CORS 头部。例如,为login动作增加Access-Control-Allow-Credentials参数如下所示:
use yii\filters\Cors; use yii\helpers\ArrayHelper; public function behaviors() { return ArrayHelper::merge([ [ 'class' => Cors::className(), 'cors' => [ 'Origin' => ['http://www.myserver.net'], 'Access-Control-Request-Method' => ['GET', 'HEAD', 'OPTIONS'], ], 'actions' => [ 'login' => [ 'Access-Control-Allow-Credentials' => true, ] ] ], ], parent::behaviors()); }