Middleware trong Laravel: lớp lọc cho mọi request

  • author-image

    Nguyễn Đình Cường

  • blog-tag
  • blog-comment 0 comment
  • created-date 09 Nov, 2026
blog-thumbnail

Trước khi một request chạm tới controller, nó có thể đi qua nhiều trạm kiểm soát: đã đăng nhập chưa, có đủ quyền không, có gửi quá nhiều lần không? Mỗi trạm đó là một middleware. Hiểu middleware là hiểu cách Laravel xử lý mọi yêu cầu HTTP.

Middleware hoạt động như thế nào?

Hãy tưởng tượng một chiếc hành lang có nhiều cánh cửa. Request đi vào, qua từng cánh cửa theo thứ tự, tới controller rồi phản hồi đi ngược ra qua các cửa đó. Mỗi middleware có thể làm việc trước khi gọi tiếp (kiểm tra, từ chối), hoặc sau khi nhận phản hồi (thêm header, ghi log).

Laravel có sẵn nhiều middleware: auth (yêu cầu đăng nhập), throttle (giới hạn tần suất), verified (yêu cầu xác minh email), can (kiểm tra quyền)...

Tự viết một middleware

Giả sử bạn muốn chỉ cho phép quản trị viên vào một nhóm route. Tạo middleware:

php artisan make:middleware EnsureUserIsAdmin
class EnsureUserIsAdmin{    public function handle(Request $request, Closure $next): Response    {        if (! $request->user()?->is_admin) {            abort(403, 'Bạn không có quyền truy cập.');        }        return $next($request);    }}

Lời gọi $next($request) chính là chuyển request sang tầng tiếp theo. Nếu bạn không gọi nó, request dừng lại tại đây.

Đăng ký và gắn vào route

Từ Laravel 11, bạn đăng ký alias trong bootstrap/app.php:

->withMiddleware(function (Middleware $middleware) {    $middleware->alias([        'admin' => \App\Http\Middleware\EnsureUserIsAdmin::class,    ]);})

Ở các phiên bản cũ hơn, việc này nằm trong app/Http/Kernel.php. Sau khi đăng ký, gắn vào route rất đơn giản:

Route::middleware(['auth', 'admin'])->prefix('admin')->group(function () {    Route::get('/users', [UserController::class, 'index']);});

Middleware có tham số và chạy sau phản hồi

Bạn có thể truyền tham số sau dấu hai chấm, ví dụ role:editor,admin, và nhận chúng làm đối số thêm của handle:

public function handle(Request $request, Closure $next, string ...$roles): Response{    if (! in_array($request->user()->role, $roles, true)) {        abort(403);    }    return $next($request);}

Để xử lý sau khi controller chạy xong, lấy phản hồi trước rồi chỉnh sửa:

$response = $next($request);$response->headers->set('X-Frame-Options', 'DENY');return $response;

Thứ tự và phạm vi

  • Middleware toàn cục: chạy với mọi request, ví dụ xử lý CORS hoặc cắt khoảng trắng.
  • Nhóm middleware: như web (session, CSRF) và api (throttle), áp dụng cho cả nhóm route.
  • Middleware theo route: chỉ gắn khi cần, giúp tránh tốn tài nguyên.

Thứ tự rất quan trọng. Ví dụ middleware kiểm tra quyền phải chạy sau auth, vì nó cần biết người dùng là ai. Khi gặp lỗi lạ, hãy kiểm tra xem thứ tự có hợp lý không.

Khi nào nên dùng middleware?

Dùng middleware cho các mối quan tâm cắt ngang nhiều route: xác thực, phân quyền thô, giới hạn tần suất, đặt ngôn ngữ, ghi log request. Không nên nhét logic nghiệp vụ vào đó, vì middleware khó kiểm thử hơn và làm luồng xử lý trở nên mờ ám. Quy tắc nhỏ: nếu logic chỉ liên quan một hành động cụ thể, hãy để ở controller hoặc service.

Ví dụ thực tế: ghi log thời gian xử lý

Một middleware rất hữu ích cho người mới là đo thời gian mỗi request, giúp bạn phát hiện endpoint chậm:

class LogRequestTime{    public function handle(Request $request, Closure $next): Response    {        $start = microtime(true);        $response = $next($request);        $ms = round((microtime(true) - $start) * 1000);        Log::info('request', [            'path'   => $request->path(),            'status' => $response->getStatusCode(),            'ms'     => $ms,        ]);        return $response;    }}

Chỉ với khoảng mười dòng, bạn có số liệu để quyết định nên tối ưu endpoint nào trước. Đây là minh họa rõ ràng cho việc middleware có thể làm việc cả trước lẫn sau controller.

Kiểm thử middleware

Bạn không cần dựng cả ứng dụng để kiểm thử middleware. Chỉ cần gọi route được bảo vệ bằng người dùng thường rồi bằng quản trị viên, và khẳng định mã trả về lần lượt là 403 và 200. Hai test nhỏ này đủ bảo vệ bạn khỏi việc vô tình mở toang một khu vực quản trị khi ai đó đổi cấu hình route.

Những lỗi thường gặp khi dùng middleware

Lỗi phổ biến nhất là quên trả về $next($request), khiến request không bao giờ đến controller và ứng dụng trả về trang trống. Lỗi thứ hai là gắn sai thứ tự, ví dụ kiểm tra quyền trước khi xác thực. Lỗi thứ ba là đặt quá nhiều việc vào middleware toàn cục, làm mọi request chậm đi dù chỉ một vài route cần đến. Hãy giữ middleware nhỏ, mỗi cái một nhiệm vụ rõ ràng, và đặt tên theo điều nó đảm bảo như EnsureUserIsAdmin.

Kết luận

Middleware là cơ chế gọn gàng để áp dụng quy tắc chung cho nhiều request mà không lặp code. Hãy thử tự viết một middleware ghi log thời gian xử lý của mỗi request, bạn sẽ nắm rất nhanh cách đường ống này vận hành.

Nếu muốn luyện tập cùng bài tập thực tế, hãy ghé các khóa học tại codebem.com khi bạn sẵn sàng.

author_photo
Nguyễn Đình Cường

0 comment