Laravel Middleware cơ chế bộ lọc trung gian

Cơ chế hoạt động của Laravel Middleware

Giới thiệu Laravel Middleware

Middleware đúng như ý nghĩa từ tên gọi của nó, là đoạn code trung gian đứng giữa request và response (xem hình trên), cung cấp một cơ chế bộ lọc. Ví dụ: Laravel cung cấp một middleware để xác nhận xem một người dùng đã xác thực chưa, nếu người dùng đã qua xác thực sẽ được chuyển hướng đến trang quản trị hoặc nếu chưa xác thực sẽ được chuyển đến trang đăng nhập.

Tất cả các middleware đều nằm trong thư mục app/Http/Middleware, để tạo một middleware mới sử dụng câu lệnh:

php artisan make:middleware <middleware-name>

Để hiểu rõ hơn middleware chúng ta sẽ bắt đầu với một ví dụ: Tạo một middleware để kiểm tra xem người dùng đủ tuổi đăng ký tài khoản chưa, nếu người dùng lớn hơn 18 tuổi thì sẽ chuyển đến trang quản trị, nếu người dùng chưa đủ tuổi tiếp tục xử lý tiếp.

Bước 1: Tạo middleware với tên là CheckAge

php artisan make:middleware CheckAge
Middleware created successfully.

Bước 2: Sau khi lệnh tạo middleware hoàn thành sẽ tạo ra một file CheckAge.php nằm trong thư muc app/Http/Middleware, thêm đoạn code kiểm tra tuổi như sau:

<?php

namespace Adshare\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)
    {
        $user = Auth::user();
        if($user->age >= 18){
            return redirect('dashboard');
        }
        return $next($request);
    }
}

Ở đây khi tuổi nhỏ hơn 18 sẽ đơn giản là gọi đến một hàm callback $next với đầu vào là $request tức là nếu nhỏ hơn 18 tuổi thì route áp dụng Middleware này sẽ tiếp tục phần mã trong route.

Đăng ký Laravel Middleware với hệ thống

Chúng ta cần đăng ký middleware với hệ thống trước khi sử dụng chúng, có hai loại middleware:

  • Global middleware là middleware sẽ được sử dụng với bất kể yêu cầu HTTP đến hệ thống, đơn giản là thêm middleware này vào thuộc tính $middleware trong class app/Http/Kernel.php.
  • Route middleware là gắn một middleware với một route xác định, trước khi gắn một route với một middleware phải liệt kê chúng vào thuộc tính $routeMiddleware trong class app/Http/Kernel.php.

Chúng xem nội dung file app/Http/Kernel.php xem cách đăng ký Middleware:

<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{

    protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::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,
        'checkage' => \App\Http\Middleware\CheckAge::class,
    ];
}

Khi middleware đã được định nghĩa trong HTTP Kernel chúng ta có thể gán chúng với các route:

Route::get('/dashboard', function () {
    // Mã xử lý khác viết ở đây
})->middleware('checkage');

Có thể sử dụng nhiều middleware cho một route

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

Cũng có thể sử dụng đầy đủ tên lớp khi gán Middleware

use App\Http\Middleware\CheckAge;
Route::get('/dashboard', function () {
    //
})->middleware(CheckAge::class);

Truyền tham số cho Middleware

Các middleware cho phép truyền thêm tham số khi được gọi, ví dụ: Người dùng trong hệ thống có rất nhiều vai trò như user, admin, super admin… chúng ta muốn xác thực người dùng dựa trên vai trò thì chúng ta cần truyền tham số cho Middleware. Sau đây chúng ta sẽ thực hành tạo một middleware và truyền tham số cho nó.

Bước 1: Tạo middleware RoleMiddleware

c:\xampp\htdocs\laravel-test>php artisan make:middleware RoleMiddleware
Middleware created successfully.

Nó sẽ tạo ra file RoleMiddleware.php trong app/Http/Middleware, sửa nội dung file này như sau:

<?php
namespace App\Http\Middleware;
use Closure;
class RoleMiddleware
{
    /**
     * Handle the incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @param  string  $role
     * @return mixed
     */
    public function handle($request, Closure $next, $role)
    {
        echo 'Vai trò:' . $role;
        return $next($request);
    }
}

Bước 2: Đăng ký RoleMiddleware trong app/Http/Kernel.php

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,
        'role' => \App\Http\Middleware\RoleMiddleware::class,
    ];

Bước 3: Tạo MainController

Laravel Controller sẽ được giới thiệu đến bạn trong bài tiếp theo, trong bài viết này bạn chưa cần hiểu Controller là gì mà cứ thực hiện theo như sau:

c:\xampp\htdocs\laravel-test>php artisan make:controller MainController
Controller created successfully.

Lệnh này sẽ tạo ra file MainController.php nằm trong thư mục app\Http\Controllers, bạn thêm nội dung vào cho file này như sau:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class MainController extends Controller
{
    public function checkRole(){
      	echo "<br>Main Controller: checkRole function";
   }
}

Bước 4: Gán route cho RoleMiddleware

Route::get('/role',[
   'middleware' => 'role:superadmin',
   'uses' => 'MainController@checkRole',
]);

Bây giờ chạy thử http://laravel.dev/role chúng ta sẽ thấy màn hình sau:

Truyền tham số cho middleware trong Laravel

Chúng ta để ý rằng trong hàm checkRole chúng ta chỉ in ra màn hình mỗi dòng “Main Controller: checkRole function”, tuy nhiên ở trang kết quả có thêm dòng “Vai trò:superadmin” do route này đã chạy qua bộ lọc RoleMiddleware và superadmin chính là giá trị từ route truyền vào cho RoleMiddleware.

Nhóm các Middleware

Đôi khi chúng ta muốn nhóm nhiều middleware với một key duy nhất, Laravel cho phép bạn làm điều này, xem thuộc tính middlewareGroups trong file app/Http/Kernel.php:

    /**
     * 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\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

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

Ở đây chúng ta thấy với key “web” đã nhóm lại rất nhiều các Middleware khác nhau. Khi đó gắn middleware cho các route đơn giản như sau:

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

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

Terminable Middleware

Đôi khi một Middleware cần làm vài việc sau khi HTTP response được gửi đến trình duyệt, ví dụ một middleware sẽ ghi dữ liệu session vào storage khi HTTP response đã gửi đến trình duyệt. Để làm việc này bạn chỉ cần định nghĩa một hàm tên là terminate trong Middleware đó, nó sẽ được gọi đến khi HTTP response gửi đến trình duyệt. Chúng ta sẽ cùng nhau thực hành:

Trong RoleMiddleware ở trên thêm nội dung như sau:

<?php

namespace App\Http\Middleware;

use Closure;

class RoleMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next, $role)
    {
        echo '1. Middleware';
        echo '<br>Vai trò:' . $role;
        echo '<br>Thực hiện khi đang xử lý HTTP response';
        return $next($request);
    }

    public function terminate($request, $response)
    {
        echo '<br>3. Terminable Middleware';
        echo '<br>Thực hiện sau khi HTTP response gửi đến trình duyệt';
    }
}

Vì RoleMiddleware đã được đăng ký ở trong lần thực hành trước nên chúng ta không cần làm thêm gì. Tiếp theo, thay đổi nội dung app/Http/Controllers/MainController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class MainController extends Controller
{
    public function checkRole(){
      	echo "<br>2. MainController@checkRole";
      	echo "<br>Main Controller: checkRole function";
      	echo "<br>Thực hiện sau khi qua bộ lọc Middleware và trước khi gửi HTTP response";
   }
}

Ok, chúng ta chạy đường dẫn http://laravel.dev/role sẽ thấy được thứ tự thực hiện các thành phần:

Terminable Middleware trong Laravel

Vậy là bạn đã nắm được khái niệm về Middleware trong Laravel cũng như thứ tự thực hiện các đoạn code, phải công nhận Laravel đưa ra các khái niệm rất đơn giản, dễ hiểu, dễ áp dụng và đặc biệt nó kiểm soát được tất cả các trạng thái từ khi phát sinh một yêu cầu tải trang đến khi kết thúc trả về kết quả cho trình duyệt. Trong bài viết có nhắc đến Controller và còn để ngỏ, chúng ta sẽ tìm hiểu Laravel Controller trong loạt bài kế tiếp.

4 thoughts on “Laravel Middleware cơ chế bộ lọc trung gian

    1. Có đoạn: “Vì RoleMiddleware đã được đăng ký ở trong lần thực hành trước nên chúng ta không cần làm thêm gì.” Bạn xem lại “Bước 2: Đăng ký RoleMiddleware trong app/Http/Kernel.php”.

  1. mình làm tới đây nhưng mà khi chạy nó báo lỗi
    ” Bây giờ chạy thử http://laravel.dev/role chúng ta sẽ thấy màn hình sau: ”
    chạy lên nó báo lỗi là
    ” ReflectionException
    Function () does not exist ”
    là do sao vậy add

Add Comment