控制器過濾器?
控制器過濾器可以是在控制器運(yùn)行前或者運(yùn)行后執(zhí)行相應(yīng)的操作,與 事件 不同,你可以非常簡(jiǎn)單、方便的選擇在應(yīng)用程序的哪個(gè) URI 上應(yīng)用過濾器。 過濾器可以修改傳入的請(qǐng)求,也可以對(duì)響應(yīng)做出修改,從而具有很大的靈活性和功能性。我們可以使用過濾器執(zhí)行一些共同的常見的任務(wù),例如:
- 對(duì)于傳入的請(qǐng)求執(zhí)行 CSRF 驗(yàn)證
- 根據(jù)用戶角色控制顯示的功能
- 在某些功能或接口執(zhí)行請(qǐng)求速率限制
- 顯示 “停機(jī)維護(hù)” 頁(yè)面
- 自動(dòng)執(zhí)行內(nèi)容協(xié)商操作(例如設(shè)置 Accept-Language 值)
- 更多
創(chuàng)建過濾器?
過濾器類必須實(shí)現(xiàn) CodeIgniter\Filters\FilterInterface
接口。
過濾器類必須有 2 個(gè)方法:before()
和 after()
,它們會(huì)在控制器運(yùn)行之前和之后執(zhí)行。
如果你的業(yè)務(wù)只需要其中一個(gè)方法,那另外的方法留空即可,不可以刪除。
一個(gè)標(biāo)準(zhǔn)的過濾器類模板如下:
<?php namespace App\Filters;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\Filters\FilterInterface;
class MyFilter implements FilterInterface
{
public function before(RequestInterface $request)
{
// Do something here
}
//--------------------------------------------------------------------
public function after(RequestInterface $request, ResponseInterface $response)
{
// Do something here
}
}
前置過濾器?
任何過濾器,你都可以返回 $request
對(duì)象并且可以對(duì)當(dāng)前的請(qǐng)求進(jìn)行更改替換,這些更改在后續(xù)的控制器執(zhí)行時(shí),仍然有效。
因?yàn)槭乔爸眠^濾器,它會(huì)在控制器被執(zhí)行前觸發(fā),所以你有時(shí)會(huì)希望做一些驗(yàn)證操作,不執(zhí)行后續(xù)的控制器,例如登錄驗(yàn)證。那么你可以通過返回不是請(qǐng)求對(duì)象的任何形式來做到這一點(diǎn)。 通常是執(zhí)行重定向。 例如以下的示例:
public function before(RequestInterface $request)
{
$auth = service('auth');
if (! $auth->isLoggedIn())
{
return redirect('login');
}
}
如果返回了 Response
對(duì)象,那么 Response
對(duì)象會(huì)發(fā)送到客戶端,并且程序會(huì)停止運(yùn)行。這對(duì)實(shí)現(xiàn) API 速率限制很有作用,詳細(xì)可以參考
app/Filters/Throttle.php 相關(guān)示例。
配置過濾器?
創(chuàng)建完過濾器后,你需要在 app/Config/Filters.php
配置它的運(yùn)行時(shí)機(jī)。該文件包含了 4 個(gè)屬性,可以精確控制過濾器的運(yùn)行時(shí)機(jī)。
$aliases?
$aliases
數(shù)組可以將一個(gè)簡(jiǎn)單的名稱與一個(gè)或多個(gè)完整類的路徑進(jìn)行綁定關(guān)聯(lián),這些完整的類就是需要運(yùn)行的過濾器:
public $aliases = [
'csrf' => \CodeIgniter\Filters\CSRF::class
];
別名是強(qiáng)制性的,如果你嘗試使用完整的類名,系統(tǒng)會(huì)觸發(fā)一個(gè)錯(cuò)誤。以別名方式定義,可以很容易的切換實(shí)現(xiàn)類。例如當(dāng)你需要替換其他過濾器時(shí),只需 要更改別名對(duì)應(yīng)的類即可。
當(dāng)然,你也可以將多個(gè)過濾器綁定到一個(gè)別名中,這樣可以使復(fù)雜的過濾器組變得簡(jiǎn)單:
public $aliases = [
'apiPrep' => [
\App\Filters\Negotiate::class,
\App\Filters\ApiAuth::class
]
];
你可以在 $aliases
中定義多個(gè)別名以滿足系統(tǒng)需求。
$globals?
這部分允許你定義應(yīng)用程序中每個(gè)請(qǐng)求需要經(jīng)過的過濾器。
請(qǐng)一定要注意過濾器的數(shù)量,因?yàn)樗械恼?qǐng)求都將經(jīng)過這些過濾器,過多會(huì)導(dǎo)致影響性能??梢栽?before
和 after
中添加別名來指定
過濾器:
public $globals = [
'before' => [
'csrf'
],
'after' => []
];
有時(shí)候你希望對(duì)絕大多數(shù)請(qǐng)求都使用過濾器處理,但個(gè)別請(qǐng)求需要單獨(dú)處理時(shí),這樣的情況很常見。
一個(gè)常見的場(chǎng)景,你需要在CSRF預(yù)防過濾器中排除一些請(qǐng)求,例如來自第三方的請(qǐng)求或者特定的 URI 地址,其他請(qǐng)求則必須經(jīng)過 CSRF
驗(yàn)證。
那么,我們可以通過 except
來實(shí)現(xiàn),可以定義一個(gè)或多個(gè)排除的 URI 地址:
public $globals = [
'before' => [
'csrf' => ['except' => 'api/*']
],
'after' => []
];
可以設(shè)置任意完整的 URI,也可以使用正則表達(dá)式,或者像本示例一樣,設(shè)置 星號(hào)* 通配符的形式來設(shè)置。這樣以 api/
開頭的所有請(qǐng)求都將不受 CSRF
過濾器的保護(hù)。但該應(yīng)用程序的其他請(qǐng)求不受影響。如果你需要指定多個(gè) URI,可以使用數(shù)組的形式即可,具體可以參考示例:
public $globals = [
'before' => [
'csrf' => ['except' => ['foo/*', 'bar/*']]
],
'after' => []
];
$methods?
你可以將過濾器應(yīng)用于請(qǐng)求的某些方法,例如 POST、GET、PUT等,在數(shù)組中使用全部小寫的形式指定過濾器名稱,與 $globals
或 $filters
屬性設(shè)置目的不同,這些過濾器全部都是前置過濾器,也就是說都在控制器運(yùn)行前執(zhí)行:
public $methods = [
'post' => ['foo', 'bar'],
'get' => ['baz']
]
除標(biāo)準(zhǔn)的 HTTP 方法外,還支持兩種特殊的方法:’cli’ 和 ‘a(chǎn)jax’。它們是所有的 ‘cli’ 命令行運(yùn)行的請(qǐng)求和 AJAX 請(qǐng)求。
注解
AJAX 請(qǐng)求的界定在 X-Requested-With
標(biāo)志,在某些情況下,X-Requested-With
不會(huì)通過 JavaScript 的 XHR 請(qǐng)求發(fā)送到后端,從而導(dǎo)致過濾器無法執(zhí)行。如何避免此類問題,請(qǐng)參照文檔的 AJAX 請(qǐng)求 章節(jié)。
默認(rèn)提供的過濾器?
CodeIgniter4 默認(rèn)綁定了三個(gè)過濾器:Honeypot、Security 和 DebugToolbar。