Skip to content

Middleware

Giới thiệu (Introduction)

Middleware cung cấp cơ chế thuận tiện để kiểm tra và lọc HTTP requests đến ứng dụng. Ví dụ, Laravel bao gồm middleware xác minh user đã authenticated. Nếu chưa, middleware chuyển hướng user đến màn hình đăng nhập. Nếu đã authenticated, middleware cho phép request đi sâu hơn vào ứng dụng.

Middleware bổ sung có thể thực hiện nhiều tác vụ ngoài authentication. Ví dụ, logging middleware có thể ghi lại tất cả requests. Laravel đã bao gồm middleware cho authentication, CSRF protection; tuy nhiên, tất cả middleware do người dùng định nghĩa thường nằm trong thư mục app/Http/Middleware.

Định nghĩa Middleware (Defining Middleware)

Tạo middleware mới bằng lệnh Artisan make:middleware:

bash
php artisan make:middleware EnsureTokenIsValid

Ví dụ — chỉ cho phép truy cập nếu token khớp:

php
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class EnsureTokenIsValid
{
    /**
     * Xử lý incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        if ($request->input('token') !== 'my-secret-token') {
            return redirect('/home');
        }

        return $next($request);
    }
}

Hãy hình dung middleware như chuỗi "layers" (tầng) mà HTTP requests phải đi qua trước khi đến ứng dụng. Mỗi layer có thể kiểm tra request và thậm chí từ chối hoàn toàn.

Tất cả middleware được phân giải qua service container, cho phép type-hint dependencies trong constructor.

Middleware và Responses

Middleware có thể thực hiện tác vụ trước hoặc sau khi request được xử lý:

php
<?php
// Before Middleware
class BeforeMiddleware
{
    public function handle(Request $request, Closure $next): Response
    {
        // Thực hiện tác vụ TRƯỚC khi request được xử lý

        return $next($request);
    }
}

// After Middleware
class AfterMiddleware
{
    public function handle(Request $request, Closure $next): Response
    {
        $response = $next($request);

        // Thực hiện tác vụ SAU khi request được xử lý

        return $response;
    }
}

Đăng ký Middleware (Registering Middleware)

Global Middleware

Nếu muốn middleware chạy trên mọi HTTP request, thêm vào global middleware stack trong bootstrap/app.php:

php
use App\Http\Middleware\EnsureTokenIsValid;

->withMiddleware(function (Middleware $middleware): void {
    $middleware->append(EnsureTokenIsValid::class);
})

Object $middleware là instance của Illuminate\Foundation\Configuration\Middleware. Dùng append để thêm cuối danh sách, prepend để thêm đầu.

Quản lý thủ công Global Middleware mặc định

php
->withMiddleware(function (Middleware $middleware): void {
    $middleware->use([
        \Illuminate\Foundation\Http\Middleware\InvokeDeferredCallbacks::class,
        // \Illuminate\Http\Middleware\TrustHosts::class,
        \Illuminate\Http\Middleware\TrustProxies::class,
        \Illuminate\Http\Middleware\HandleCors::class,
        \Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance::class,
        \Illuminate\Http\Middleware\ValidatePostSize::class,
        \Illuminate\Foundation\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    ]);
})

Gán Middleware cho Routes

Gán middleware khi định nghĩa route:

php
use App\Http\Middleware\EnsureTokenIsValid;

Route::get('/profile', function () {
    // ...
})->middleware(EnsureTokenIsValid::class);

Gán nhiều middleware:

php
Route::get('/', function () {
    // ...
})->middleware([First::class, Second::class]);

Loại trừ Middleware

Dùng withoutMiddleware để loại trừ:

php
Route::middleware([EnsureTokenIsValid::class])->group(function () {
    Route::get('/', function () {
        // ...
    });

    Route::get('/profile', function () {
        // ...
    })->withoutMiddleware([EnsureTokenIsValid::class]);
});

Middleware Groups (Nhóm Middleware)

Laravel bao gồm nhóm middleware webapi tự động áp dụng cho routes tương ứng. Tùy chỉnh trong bootstrap/app.php:

php
->withMiddleware(function (Middleware $middleware): void {
    $middleware->web(append: [
        EnsureUserIsSubscribed::class,
    ]);
})

Hoặc thêm vào cả hai nhóm:

php
->withMiddleware(function (Middleware $middleware): void {
    $middleware->appendToGroup('web', [
        // ...
    ]);

    $middleware->prependToGroup('web', [
        // ...
    ]);
})

Middleware Aliases

Gán alias cho middleware trong bootstrap/app.php:

php
->withMiddleware(function (Middleware $middleware): void {
    $middleware->alias([
        'subscribed' => EnsureUserIsSubscribed::class,
    ]);
})

Sau đó gán bằng alias:

php
Route::get('/profile', function () {
    // ...
})->middleware('subscribed');

Sắp xếp Middleware (Sorting Middleware)

Chỉ định thứ tự ưu tiên middleware:

php
->withMiddleware(function (Middleware $middleware): void {
    $middleware->priority([
        \Illuminate\Cookie\Middleware\EncryptCookies::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class,
        \Illuminate\Routing\Middleware\ThrottleRequests::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
        \Illuminate\Auth\Middleware\Authorize::class,
    ]);
})

Middleware Parameters

Middleware có thể nhận thêm parameters. Truyền sau argument $next:

php
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class EnsureUserHasRole
{
    public function handle(Request $request, Closure $next, string $role): Response
    {
        if (! $request->user()->hasRole($role)) {
            // Redirect...
        }

        return $next($request);
    }
}

Chỉ định khi gán route, phân tách bằng ::

php
Route::put('/post/{id}', function (string $id) {
    // ...
})->middleware('role:editor');

Nhiều parameters phân tách bằng dấu phẩy:

php
Route::put('/post/{id}', function (string $id) {
    // ...
})->middleware('role:editor,publisher');

Terminable Middleware

Đôi khi middleware cần thực hiện tác vụ sau khi response đã gửi đến browser. Định nghĩa method terminate:

php
<?php

namespace Illuminate\Session\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class TerminatingMiddleware
{
    public function handle(Request $request, Closure $next): Response
    {
        return $next($request);
    }

    /**
     * Xử lý tác vụ sau khi response gửi đến browser.
     */
    public function terminate(Request $request, Response $response): void
    {
        // ...
    }
}

Method terminate nhận cả request và response. Laravel sẽ phân giải instance mới từ service container khi gọi terminate.

Nếu muốn dùng cùng instance giữa handleterminate, đăng ký middleware như singleton:

php
use App\Http\Middleware\TerminatingMiddleware;

->withMiddleware(function (Middleware $middleware): void {
    $middleware->append(TerminatingMiddleware::class);
})

// Trong AppServiceProvider
$this->app->singleton(TerminatingMiddleware::class);