Laravel Middleware: Kiến trúc và cách viết Custom Middleware hiệu quả
Đi sâu vào cơ chế Onion Architecture của Laravel Middleware. Hướng dẫn viết Custom Middleware xử lý logging, phân quyền và tối ưu response.
Khi phỏng vấn các bạn ứng viên Laravel mid/senior, tôi hay hỏi: “Middleware hoạt động như thế nào?”. Đa số trả lời: “Nó đứng giữa Request và Controller để chặn hoặc cho qua”. Đúng, nhưng chưa đủ.
Hiểu sâu về Onion Architecture (Kiến trúc củ hành) của Middleware sẽ giúp bạn làm chủ được flow của ứng dụng, đặc biệt là khi cần can thiệp vào Response hoặc xử lý các tác vụ ngầm (background tasks) ngay trong lifecycle của request.
Mô hình Củ hành (Onion)
Hãy tưởng tượng request như một mũi tên bắn xuyên qua củ hành.
- Lớp vỏ: Global Middleware (Check maintenance mode, Trim strings…).
- Lớp giữa: Group Middleware (Web/API group, Session start, CSRF verify…).
- Lớp trong: Route Middleware (Auth check, Role check…).
- Nhân: Controller xử lý chính.
Mũi tên đi vào (Request) -> Chạm Nhân -> Bật ngược ra (Response). Tức là Middleware có cơ hội can thiệp 2 lần: Trước khi vào Controller và Sau khi có Response.
public function handle($request, Closure $next)
{
// 1. BEFORE: Code chạy TRƯỚC khi vào Controller
// Ví dụ: Kiểm tra user ban, log request time start
$response = $next($request); // <-- Đi sâu vào lớp tiếp theo/Controller
// 2. AFTER: Code chạy SAU khi Controller return Response
// Ví dụ: Kèm thêm header, log request time end
return $response;
}
Terminable Middleware: Vũ khí bị lãng quên
Có một tính năng cực hay mà ít dev dùng: terminate().
Đôi khi bạn muốn log lại hoạt động user, nhưng việc ghi log vào database tốn 200ms. Bạn không muốn user phải chờ thêm 200ms đó.
Giải pháp: Dùng terminate. Method này chạy sau khi response đã được gửi xuống trình duyệt. User đã thấy web load xong rồi, server mới âm thầm chạy logic này.
class LogUserActivity
{
public function handle($request, Closure $next)
{
return $next($request);
}
public function terminate($request, $response)
{
// Code này chạy sau khi user đã nhận phản hồi
// Thoải mái query DB, bắn API logging mà không sợ delay web
ActivityLog::create([
'url' => $request->fullUrl(),
'status' => $response->getStatusCode(),
'time' => microtime(true) - LARAVEL_START,
]);
}
}
Lưu ý: Để terminate hoạt động, server của bạn phải dùng FastCGI (như PHP-FPM), điều này mặc định hầu hết server đều có.
Best Practices khi viết Middleware
- Single Responsibility: Mỗi middleware chỉ làm một việc. Đừng gộp check Auth và check Role vào chung một cái.
- Fail Fast: Nếu request không hợp lệ (ví dụ thiếu token), hãy
return response()->json(...)hoặcabort()ngay lập tức ở phần Before, đừng cho nó chui sâu vào trong tốn tài nguyên. - Alias ngắn gọn: Đặt tên alias (trong
bootstrap/app.phphoặcKernel.php) gợi nhớ. Ví dụrole:serverdễ đọc hơnCheckUserRole::class.
Ví dụ thực tế: Middleware chuẩn hóa Response
Dự án của tôi luôn có một middleware bọc ngoài cùng để đảm bảo mọi JSON response trả về đều có format chuẩn { data, status, message }, kể cả khi dev lười chỉ return array.
public function handle($request, Closure $next)
{
$response = $next($request);
// Chỉ can thiệp nếu là JSON response
if ($response instanceof JsonResponse) {
$data = $response->getData(true);
// Nếu data chưa có cấu trúc chuẩn thì bọc lại
if (!isset($data['status'])) {
$response->setData([
'status' => $response->getStatusCode() < 400,
'data' => $data,
'message' => 'Success'
]);
}
}
return $response;
}
Kết luận
Middleware không chỉ là cái “cổng bảo vệ”. Nó là công cụ mạnh mẽ để quản lý Cross-Cutting Concerns (những vấn đề cắt ngang hệ thống như Logging, Caching, Auth) một cách tập trung và gọn gàng. Hãy tận dụng tối đa terminate() để tăng tốc độ phản hồi cho user.
Cơ chế hoạt động của Middleware trong Laravel được ví như hình ảnh gì?
Thử Thách Kiến Thức Lịch Sử?
Khám phá hàng trăm câu hỏi trắc nghiệm lịch sử thú vị tại HistoQuiz. Vừa học vừa chơi, nâng cao kiến thức ngay hôm nay!