Laravel 文件閱讀:中介軟體

zhangbao發表於2017-09-17

翻譯、衍生自:https://learnku.com/docs/laravel/5.5/middleware

簡介

中介軟體用來過濾專案中的 HTTP 請求,實際上 Laravel 專案中大量使用了中介軟體。例如,Laravel 中有一個驗證使用者是否認證的中介軟體,如果使用者沒認證,就跳轉到登入頁面;如果認證了,就會允許進一步的操作。

當然,中介軟體的作用不只在認證上。CORS 中介軟體負責給專案中的響應設定正確的表頭(Headers );日誌中介軟體負責記錄專案中處理的所有請求資訊。

Laravel 框架中包含了幾個中介軟體,包括負責處理認證和 CSRF 保護的。所有這些中介軟體位於 app/Http/Middleware 目錄下。

定義中介軟體

使用 Artisan 命令 make:middleware 建立中介軟體:

php artisan make:middleware CheckAge

這個命令會在 app/Http/Middleware 目錄下建立一個 CheckAge 類。在這個中介軟體裡,我們設定年齡大於 200 歲的可以進一步操作(這不扯嗎?),小於等於 200 歲的重定向到使用者控制檯地址 /home

<?php

namespace App\Http\Middleware;

use Closure;

class CheckAge
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if ($request->age <= 200) {
            return redirect('home');
        }

        return $next($request);
    }
}

允許請求的進一步操作,只需將請求例項 $request 放入回撥 $next 中即可。

可以把中介軟體設想成,在最終到達想要執行的業務程式碼前的一層層「過濾網」,請求必須通過所有的中介軟體才能達到最終要執行的業務邏輯,否則被中間任何一箇中介軟體拒絕,都會導致請求失敗的。

中介軟體處理時機

一箇中介軟體在請求之前或者之後進行任務處理,取決於中介軟體的程式碼邏輯放置的位置。例如,下面的中介軟體在處理請求 之前 進行了一些任務操作:

<?php

namespace App\Http\Middleware;

use Closure;

class BeforeMiddleware
{
    public function handle($request, Closure $next)
    {
        // Perform action

        return $next($request);
    }
}

而下面的中介軟體是在處理請求 之後 才進行任務操作:

<?php

namespace App\Http\Middleware;

use Closure;

class AfterMiddleware
{
    public function handle($request, Closure $next)
    {
        $response = $next($request);

        // Perform action

        return $response;
    }
}

註冊中介軟體

全域性中介軟體

如果一箇中介軟體在每次的 HTTP 請求時都要用到,那麼把它列入到 app/Http/Kernel.php 檔案裡的 $middleware 陣列中即可。列入在 $middleware 陣列中的中介軟體又稱為全域性中介軟體。

為路由使用中介軟體

使用在路由身上的中介軟體是在 app/Http/Kernel.php$routeMiddleware 陣列屬性中定義的。如果你建立了一個新的給路由使用的中介軟體,就需要在它新增到 $routeMiddleware 這個陣列裡,並給這個中介軟體一個 key——相當於中介軟體的名字。

// Within App\Http\Kernel Class...

protected $routeMiddleware = [
    'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
    'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
    'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
    'can' => \Illuminate\Auth\Middleware\Authorize::class,
    'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
    'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
];

使用中介軟體時,就要用到了這個 key。

Route::get('admin/profile', function () {
    //
})->middleware('auth');

也可以在一個路由上,同時使用多箇中介軟體。

Route::get('/', function () {
    //
})->middleware('first', 'second');

也可以使用中介軟體的包含名稱空間的完整類名(又稱完全限定類名)使用中介軟體:

use App\Http\Middleware\CheckAge;

Route::get('admin/profile', function () {
    //
})->middleware(CheckAge::class);

中介軟體組

中介軟體組中包含多箇中介軟體,但它的使用和中介軟體是完全一樣的。中介軟體組在 app/Http/Kernel.php$middlewareGroups 屬性中定義,每個中介軟體組還有一個對應的 key。

Laravel 專案中,預設並使用了兩個中介軟體組:webapi,它們分別用在了 routes/web.phproutes/api.php 上,前者是定義 Web 介面路由的地方,後者是定義 API 介面路由的地方。這兩種型別的介面,各自都有一些通用的中介軟體,然後放在了一個組裡進行管理。

/**
 * The application's route middleware groups.
 *
 * @var array
 */
protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],

    'api' => [
        'throttle:60,1',
        'auth:api',
    ],
];

已經說過,中介軟體組的使用和中介軟體是完全一樣的,不過帶來一個好處——一次定義,即可使用多箇中介軟體。中介軟體組可以分配給路由和控制器 action 使用。

Route::get('/', function () {
    //
})->middleware('web');

Route::group(['middleware' => ['web']], function () {
    //
});

注意,routes/web.php 檔案裡的中介軟體已經預設使用了 web 中介軟體組,這是在 RouteServiceProvider 中定義的,上面的是例子演示,在 routes/web.php 中定義路由時,是無需為路由額外分配 web 中介軟體組的。

中介軟體引數

可以為中介軟體傳遞引數。例如,如果系統有一個需求——需要在驗證角色後才允許認證使用者的進一步操作。以下面的 CheckRole 中介軟體為例,它接受一個角色名作為引數。

中介軟體引數在 handle 方法的 $next 引數之後定義。

<?php

namespace App\Http\Middleware;

use Closure;

class CheckRole
{
    /**
     * Handle the incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @param  string  $role
     * @return mixed
     */
    public function handle($request, Closure $next, $role)
    {
        if (! $request->user()->hasRole($role)) {
            // Redirect...
        }

        return $next($request);
    }

}

使用中介軟體引數的方式是在中介軟體後面加上一個 :,然後在 : 後面跟上你要傳遞給中介軟體的引數。

Route::put('post/{id}', function ($id) {
    //
})->middleware('role:editor');

在 HTTP 響應之後……

有時,中介軟體需要在 HTTP 響應傳送到瀏覽器之後才做處理工作。例如,包含在 Laravel 中的會話中介軟體會在響應傳送到瀏覽器後,再去儲存會話資料。針對這樣的需求,需要在中介軟體中定義一個 terminate 方法,它會在響應傳送到瀏覽器之後自動被呼叫。

<?php

namespace Illuminate\Session\Middleware;

use Closure;

class StartSession
{
    public function handle($request, Closure $next)
    {
        return $next($request);
    }

    public function terminate($request, $response)
    {
        // Store the session data...
    }
}

terminate 方法接受兩個引數,請求例項和響應例項。定義好後,將這個中介軟體放在 app/Http/Kernel.php 新增在中介軟體列表中即可。

在呼叫 terminate 方法時,Laravel 是從服務容器中找到並建立一個新的中介軟體例項使用的,如果你只要使用同一個中介軟體例項,那麼就要使用容器的 singleton 方法註冊中介軟體了。

相關文章