Yii 필터 소개
필터는 컨트롤러 작업 전후에 실행되도록 구성할 수 있는 코드 조각입니다. 예를 들어, 요청된 작업을 수행하기 전에 사용자가 인증되었는지 확인하기 위해 액세스 제어 필터가 실행됩니다. 성능 필터를 사용하여 컨트롤러가 실행되는 데 걸리는 시간을 측정할 수 있습니다.
작업 하나에 여러 개의 필터가 있을 수 있습니다. 필터는 필터 목록에 나타나는 순서대로 실행됩니다. 필터는 작업 및 기타 후속 필터가 실행되는 것을 방지할 수 있습니다.
필터를 작성하는 방법에는 두 가지가 있습니다.
어떤 종류의 필터에 관계없이 어떤 필터가 어떤 작업에 작동할지 설정하려면 컨트롤러의 공개 함수인 filter() 메서드를 재정의해야 합니다.
방법 기반 필터
메서드 기반 필터를 작성하려면 세 단계가 필요합니다.
컨트롤러에 액션을 작성하세요.
컨트롤러에 필터 함수를 작성합니다. 함수 이름에는 다음과 같이 필터가 붙어야 합니다. function filterAccessControl()
필터와 액션 사이의 관계를 정의하기 위해 상위 클래스 CController의 필터() 메서드를 다시 작성하세요.
예:
<?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', // ), ); } }
맞춤 필터 클래스
필터 클래스를 맞춤설정하려면 별도의 필터 클래스를 작성하고, CFilter 클래스를 상속하고, CFilter 클래스 아래의 일부 메서드를 재정의해야 합니다. CFilter 클래스의 코드를 살펴보면 이 클래스에는 코드가 많지 않으며 여전히 이해하기 쉽습니다.맞춤 필터 예:
<?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>"; } }
컨트롤러에서 사용자 정의 필터와 작업 간의 바인딩 관계를 등록합니다.
/** * 第三步:重写父类CController的filters()方法,定义过滤器与动作的关系 * @see CController::filters() */ ublic function filters(){ return array( //定义过滤器与动作的关联关系 'addFilter + add', array( 'application.filters.TestFilter', ), );
필터를 사용자 정의했습니다. TestFilter는 CFilter 클래스를 상속하고 CFilter 클래스의 두 가지 주요 메서드인 preFilter(사전 컨트롤러, 작업이 실행되기 전에 실행됨) 및 postFilter(사후 컨트롤러, 작업이 실행된 후 실행)를 재정의합니다. ).
두 컨트롤러의 실행 순서
위에서 작성한 사용자 정의 필터 클래스를 actionAdd에 바인딩한다고 가정합니다. 그런 다음 사용자 정의 필터는 상위 클래스 CFilter에서 preFilter 및 postFilter라는 두 가지 메서드를 상속하고, 이 클래스와 바인딩된 actionAdd 사이의 실행 순서는 무엇입니까?테스트 후 실행 순서는 CFilter::preFilter--------->UserController::actionAdd--------->CFilter::postFilter입니다.
즉, 액션 실행 전후에 필터링 작업을 수행할 수 있습니다.
그럼 기사 시작 부분에 "필터는 작업 및 기타 후속 필터의 실행을 방지할 수 있습니다"라고 어떻게 나와 있나요?
CFilter::preFilter의 공식 댓글을 읽어보면 알 수 있습니다:
@return boolean 필터링 프로세스를 계속하고 작업을 실행해야 하는지 여부입니다.
true입니다. 즉, 후속 작업과 사후 필터가 기본적으로 실행됩니다. 사용자 정의 필터 클래스에 있는 경우 CFilter::preFilter 메소드를 재정의하고
를 반환합니다.
false, 후속 작업과 필터가 실행되는 것을 방지할 수 있습니다!
필터 사용
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()); }