控制器過濾器?

控制器過濾器可以是在控制器運(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)示例。

后置過濾器?

后置過濾器與前置過濾器幾乎一樣,不同的是后置過濾器只返回 $response 對(duì)象。并且,你無法停止程序的運(yùn)行。你只能對(duì) $response 對(duì)象 做一些修改,比如為了確保客戶端可以正常識(shí)別而設(shè)置某些安全選項(xiàng),或者使用緩存輸出,甚至可以使用錯(cuò)別字過濾器過濾最終的輸出內(nèi)容。

配置過濾器?

創(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)致影響性能??梢栽?beforeafter 中添加別名來指定 過濾器:

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é)。

$filters?

這個(gè)屬性是過濾器別名數(shù)組,每個(gè)別名可以定義指定 URI 的前置或后置過濾器:

public filters = [
    'foo' => ['before' => ['admin/*'], 'after' => ['users/*']],
    'bar' => ['before' => ['api/*', 'admin/*']]
];

默認(rèn)提供的過濾器?

CodeIgniter4 默認(rèn)綁定了三個(gè)過濾器:Honeypot、Security 和 DebugToolbar。