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

Đăng bởi:

Hùng Việt

Đăng ngày:

Feb 28, 2021

Đăng ở:

Tin Tức Công Nghệ

Một trong những yêu cầu chính của bất kỳ ứng dụng web nào là lọc yêu cầu HTTP và tất cả chúng ta cần thực hiện chức năng đó thật tốt. Laravel PHP Framework cũng cung cấp chức năng đó và khái niệm này được gọi là Middle Laravel Middleware.

Middleware dùng để làm gì ?

Trước khi đi vào việc tạo và sử dụng Middleware trong Laravel thì chúng ta sẽ cùng tìm hiểu về công dụng của nó qua. Giả sử chúng ta có một trang web xem phim trả phí với một vài tính năng như:

  • Xem danh sách các bộ phim mà hệ thống của bạn có
  • Xem một bộ phim
  • Download phim về máy

Và tương ứng chúng ta sẽ có vài chức năng như sau:

class MovieController extends Controller
{
    public function getList(Request $request)
    {
        // Lấy danh sách các bộ phim
    }

    public function watchMovie(Request $request)
    {
        // Xem một bộ phim
    }

    public function downloadMovie(Request $request)
    {
        // Download bộ phim bạn chọn
    }
}

 

Vì trang web chúng ta là trang web phải trả phí nên đồng nghĩa với việc người dùng phải là tài khoản đã đăng nhập rồi để có thể thực hiện chức năng nói trên. Với cách làm thông thường thì ta có thể thêm đoạn code kiểm trả xem người dùng đã đăng nhập chưa ở mỗi chức năng như sau:

class MovieController extends Controller
{
    public function getList(Request $request)
    {
        if (Auth::check()) {
            // Lấy danh sách các bộ phim
        }
    }

    public function watchMovie(Request $request)
    {
        if (Auth::check()) {
            // Xem một bộ phim
        }
    }

    public function downloadMovie(Request $request)
    {
        if (Auth::check()) {
           // Download bộ phim bạn chọn
        }
    }
}

Nếu trang web của bạn chỉ có 3 chức năng thì cũng ok không có vấn đề gì to tát lắm. Nhưng thử tưởng tượng bạn có đến 50 chức năng đều cần kiểm tra đăng nhập thì sao ? Bạn có thể vẫn try-hard bằng cách copy lại phần code kia cho cả 50 chức năng như trên và mọi thứ lại chạy đâu vào đó. Tuy nhiên một hôm đẹp trời, 50 tính năng này của bạn lại cần phải check xem nếu người dùng đã đăng nhập và trong tài khoản trong phim phải có lớn hơn 100k VNĐ thì sao.

Để giải quyết bài toán như nói trên thì chúng ta sẽ có khái niệm Web Middleware . Đơn giản nhất bạn có thể hiểu nó phần trong ứng dụng của bạn cho phép bạn gom toàn bộ 50 lần đoạn code nói trên về chung một nột chức năng duy nhất và có thể tái sử dụng lại. Middleware sẽ có vai trò đứng giữa các request từ người dùng đến hệ thống của bạn và kiểm tra xem nó có thỏa mãn các điều kiện mà bạn mong muốn trước khi có thể truy cập vào tính năng mà bạn cung câp:

  • Người dùng đã đăng nhập
  • Tài khoảng của người dùng có > 100k VNĐ
393908b1-8c32-46ac-9862-7da74a262a1e
 
Như bạn thấy tất cả những yêu cầu của người dùng đều phải đi qua đây trước khi có thể thực hiện các tính năng mà bạn cung cấp. Đồng nghĩa với việc ở đây, chúng ta có thể lầm bất cứ thứ gì mà chúng ta muốn với request này trước khi bạn cho nó tiếp tục di chuyển đến tính năng của bạn hoặc từ chối không cho nó đi tiếp.
 

Middleware trong Laravel

Để tạo mới một middleware, ta sử dụng cú pháp:

php artisan make:middleware [MiddlewareName]

ở đây giả sử mình sẽ tạo một middleware để kiểm tra trường hợp mà chúng ta nói trên như sau:

php artisan make:middleware VerifyAccountBalance

Sau khi bạn chạy lệnh trên thì Laravel sẽ tạo ra cho chúng ta một file mới nằm trong đường dẫn app/Http/Middleware/VerifyAccountBalance.php và có nội dung như sau:

 

<?php

namespace App\Http\Middleware;

use Closure;

class VerifyAccountBalance
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        return $next($request);
    }
}

Middleware mà chúng ta tạo ra ở đây chỉ có duy nhất một function là handle() và 2 tham số truyền vào lần lượt là $request - request từ phía người dùng và một closure tên là $next dùng để cho request của người dùng tiếp tục truy cập đến chức năng trong ứng dụng của bạn khi nó thỏa mãn hết các yêu cầu bạn đặt ra. Bạn có thể hiểu phần return $next($reuqest); sẽ cho phép request của người dùng được tiếp tục được thực hiện. Như ví dụ từ trước đó ta sẽ cần kiểm tra 2 thứ đó là người dùng đã đăng nhập và tài khoản có lớn hơn 100K VND. Ta có thể viết lại nội dung hàm handle() như sau:

public function handle($request, Closure $next)
{
    if (Auth::check() && Auth()::user()->getAccountBalance() > 100000) {
        return $next($request);
    }
    return redirect('home')->with('message', __('Some error message'));
}

Ở đoạn code trên thì nếu request từ phía người dùng thỏa mãn 2 điều kiện mà chúng ta đặt ra thì chúng ta sẽ cho họ tiếp tục truy cập vào tính năng còn trong trường hợp ngược lại thì chúng ta sẽ redirect người dùng quay về trang home kèm theo một đoạn message nào đó mà bạn mong muốn. Đến đây là chúng ta đã tạo ra được một Middleware để gom nhóm việc kiểm tra này lại vào trong một class rồi. Tiếp đến để dùng được nó thì ta sẽ cần khai báo nó trong file app/Http/Kernel.php. Bạn mở file này lên sẽ thấy có rất nhiều nội dung trong đó rồi nhưng chúng ta sẽ chưa bàn nó ở đây mà ta chỉ tập chung vào phần này:

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

Đây là nơi mà chúng ta sẽ khai báo các middleware để có thể dùng trong file routes/web.php của chúng ta, mặc định nó đã có rất nhiều các middleware khác mà Laravel đã tạo sẵn cho chúng ta để sử dụng nhưng ta sẽ không nói đến ở đây. Để thêm middleware mà chúng ta mới tạo, bạn chỉ cần thêm nội dung sau vào dưới đoạn code nói trên:

'verfiy-account-balance' => App\Http\Middleware\VerifyAccountBalance::class,

Giờ đây thay vì bạn phải copy đi copy lại đoạn code kiểm tra 2 điều kiện mà bạn đặt ra trong tất các các hàm ở các controller thì bạn có thể đơn giản hóa nó ngay từ trong router như sau:

Route::group(['middleware' => 'verfiy-account-balance'], function() {
    Route::get('get-list-movie', 'MovieController@getList');
    Route::get('watch-movie', 'MovieController@watchMovie');
    Route::get('download-movie', 'MovieController@downloadMovie');
    // 47 chức năng khác
});

ở đây bạn nhớ đặt phần 'middleware' => 'verfiy-account-balance' trùng với tên mà chúng ta đã khai báo trong file Kernel.php. Giờ đây trước khi request từ người dùng được sử lý trong controller của chúng ta thì nó sẽ được kiểm tra theo điều kiện mà chúng ta đặt ra rồi mới có thể đi tiếp. Ngoài cách viết như trên thì để sử dụng middleware cho một route duy nhất thì ta cũng có thể viết:

Route::get('get-list-movie', 'MovieController@getList')->middleware('verfiy-account-balance');

Sử dụng middleware đã có sẵn của Laravel

Ở trong file Kernel.php bạn sẽ thấy có một số phần như sau:

protected $middleware = [
    \App\Http\Middleware\TrustProxies::class,
    \App\Http\Middleware\CheckForMaintenanceMode::class,
    \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
    \App\Http\Middleware\TrimStrings::class,
    \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];

Đây là các middleware dạng global nghĩa là tất cả các request từ phía người dùng sẽ được đi qua các class nói trên trước khi đi tiếp đến với chức năng của bạn

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 là các middleware đã được gom nhóm lại và bạn có thể dụng nó để áp dụng cho các route của bạn. Mặc định thì toàn bộ các route bạn khai báo trong file web.php sẽ đều đi qua nhóm middleware web còn toàn bộ route trong file api.php sẽ đi qua nhóm middleware api

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

Còn đây là các middleware riêng lẻ mà bạn có thể sử dụng luôn ở trong code của mình. Cơ bản nhất là ở đây đã có sẵn middleware để kiểm tra việc người dung đã đăng nhập hay chưa là 'auth' => \App\Http\Middleware\Authenticate::class. Chính vì thế trong middleware VerifyAccountBalance ta sẽ bỏ bớt phần kiểm tra người dùng đã đăng nhập đi như sau:

public function handle($request, Closure $next)
{
    if (Auth()::user()->getAccountBalance() > 100000) {
        return $next($request);
    }
    return redirect('home')->with('message', __('Some error message'));
}

Lúc này trong file web.php phần khai báo route ta sẽ sửa lại một chút thành:

Route::group(['middleware' => ['auth', 'verfiy-account-balance']], function() {
    Route::get('get-list-movie', 'MovieController@getList');
    Route::get('watch-movie', 'MovieController@watchMovie');
    Route::get('download-movie', 'MovieController@downloadMovie');
    // 47 chức năng khác
});

Lưu ý ở đây bạn có thể sử dụng mảng để có thể dùng nhiều middleware cùng lúc và thứ tự chạy của các middleware sẽ theo thứ tự trong mảng.

Kết bài

Đến đây theo mình là đã có đủ các kiến thức cơ bản và hay dùng với middleware trong Laravel rồi nên mình xin phép dừng bài viết ở đây

 

Tài liệu tham khảo

https://laravel.com/docs/7.x/middleware

default_image
Tác giả: Hùng Việt
ADMIN

Bình luận

name
Code Dởm 23.02.2021 lúc 15:56

Thanks chủ tus, bài viết hữu ích

Để lại bình luận

Email và số điện thoại sẽ không được công khai. Những trường bắt buộc được đánh dấu *

Repository deleted Your repository has remove
Loading