Tìm hiểu Laravel (P8) - Middleware

Indexs

  • Ở phần trước ta đã cùng nhau tìm hiểu về Routing nơi định nghĩa các định tuyến, quyết định các request sẽ được điều hướng đến controller nào, action nào. Nhưng trước đó để request được thực hiện thì các request sẽ phải đi qua một phần được gọi là Middleware (trung gian), ở phần này ta sẽ cùng nhau tìm hiểu Middleware.

1. Giới thiệu

  • Middleware cung cấp một số cơ chế thuận tiện cho việc lọc các HTTP request vào ứng dụng của ta. Chẳng hạn, Laravel đã có sẵn middleware xác thực người dùng đăng nhập vào hệ thống khi ta khởi tạo project, nếu người dùng đã đăng nhập thì sẽ cho phép thực hiện các request của người dùng, còn không sẽ chuyển hướng đến trang login và yêu cầu đăng nhập.
  • Tất nhiên ngoài những middleware đã được laravel cung cấp sẵn ta cũng có thể thêm các middleware theo mục đích riêng, và tất cả các middleware sẽ nằm trong thư mục app/Http/Middleware.

2. Định nghĩa Middleware

  • Để tạo mới một middleware, ta sẽ dùng lệnh artisan make:middleware:
    php artisan make:middleware CheckAge
  • Lệnh này sẽ tạo ra một class mới CheckAge trong thư mục app/Http/Middleware. Middleware này chỉ cho phép các request cung cấp age lớn hơn 200, còn không sẽ chuyển hướng người dùng đến trang home.
    namespace App\Http\Middleware;

    use Closure;

    class CheckAge
    {
        /**
         * Run the request filter.
         *
         * @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);
        }

    }
  • Với đoạn mã trên, nếu age nhỏ hơn hoặc bằng 200, middleware sẽ trả lại request điều hướng người dùng đến trang home, ngược lại request sẽ được gửi đi tiếp để xử lý bằng cách gọi callback $next với $request.
  • Đến đây ta có thể hình dung nôm na middleware là một chuỗi các "layers" trên HTTP request cần phải đi qua trước khi đi vào trong chương trình. Mỗi layer sẽ thực hiện kiểm tra request và thậm chí có thể huỷ từ chối request hoàn toàn tùy thuộc các trường hợp cụ thể.

3. Middleware chạy trước hay sau một request

  • Rõ ràng theo cách giải thích và định nghĩa một middleware như phần trên thì ta có thể khẳng định middleware sẽ được thực hiện trước khi một request được thực hiện, chẳng hạn middleware dưới đây sẽ thực hiện vài tác vụ trước khi request được chương trình xử lý:
    namespace App\Http\Middleware;

    use Closure;

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

            return $next($request);
        }
    }
  • Tuy nhiên nhiều trường hợp ta lại muốn sau khi request được thực hiện xong thì mới thực hiện vài tác vụ, chẳng hạn như việc ghi lại log. Khi đó ta có thể định nghĩa middleware như sau:
    namespace App\Http\Middleware;

    use Closure;

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

            // Perform action

            return $response;
        }
    }

4. Đăng ký middleware

  • Global Middleware: Để một middleware có thể thực thi trong mỗi HTTP request tới hệ thống, đơn giản ta chỉ cần thêm tên class của middleware đó vào trong thuộc tính middleware của class app/Http/Kernel.php.
  • Gán middleware cho route
  • Nếu bạn muốn thiết lập middleware cho một số route cụ thể, bạn đầu tiên cần phải thêm middleware vào trong biến $routeMiddleware trong file app/Http/Kernel.php và đặt cho nó một 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,
    ];
  • Khi middleware đã được gán với một key trong kernel, ta có thể dùng key này để gán middleware cho route dùng phương thức middleware
    Route::get('admin/profile', function () {
        //
    })->middleware('auth');
  • Ta cũng có thể gán nhiều hơn 1 middleware cho route
    Route::get('/', function () {
        //
    })->middleware('first', 'second');
  • Ta cũng có thể khai báo và gọi tên class middleware ngay trong file route
    use App\Http\Middleware\CheckAge;

    Route::get('admin/profile', function () {
        //
    })->middleware(CheckAge::class);
  • Nhóm Middleware: Nhiều khi ta lại muốn nhóm nhiều middleware lại thành một nhóm bằng một key để thuận tiện hơn trong việc gán cho middleware cho route. Khi đó ta có thể dùng thuộc tính $middlewareGroups của HTTP kernel. Ví dụ 2 nhóm middleware mà laravel cung cấp sẵn là webapi
    /**
     * 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',
        ],
    ];
  • Việc thực hiện gán nhóm middleware cho route cũng được thực hiện tương tự
    Route::get('/', function () {
        //
    })->middleware('web');

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

5. Middleware tham số

  • Middleware cũng có thể nhận các tham số truyền vào. Ví dụ, nếu chương trình cần xác nhận user đã được xác thực có "role" cụ thể trước khi thực hiện một thao tác nào đó, bạn có thể tạo ra CheckRole để nhận tên của role như một tham số.
  • Các tham số của middleware sẽ được truyền vào thành tham số của hàm handle ngay sau tham số $next:
    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);
        }

    }
  • Tham số cho middleware cũng có thể được khai báo trên route bằng cách phân cách tên middleware và tham số bởi dấu :. Nhiều tham số phân cách nhau bởi dấu phẩy ,:
    Route::put('post/{id}', function ($id) {
        //
    })->middleware('role:editor');

6. Terminabel Middleware

  • Thi thoảng ta sẽ cần một middleware thực hiện chỉ sau khi HTTP response đã được gửi xong cho trình duyệt. Ví dụ, "session" middleware đi kèm với Laravel cung cấp session data cho storage sau khi response được gửi tới trình duyệt. Để làm được việc này, ta sẽ thêm vào hàm terminate trong middleware:
    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...
        }
    }
  • Phương thức terminate sẽ nhận cả request và response. Khi mà bạn khai báo một terminable middleware, bạn nên thêm nó vào trong danh sách global middleware trong HTTP kernel.

Tài liệu tham khảo Middleware


All Rights Reserved