Menempelkan middleware lewat nama class pada tiap route berfungsi, tetapi aplikasi nyata memakai puluhan middleware di banyak tempat. Kita butuh satu pusat kendali untuk memberi nama ringkas, menempelkan sekali untuk sekelompok route, dan memastikan urutan eksekusinya benar. Sejak Laravel 11, pusat itu adalah bootstrap/app.php lewat method withMiddleware() — tidak ada lagi file Http/Kernel.php seperti di Laravel 10 ke bawah. Chapter ini membedah seluruh pilihan registrasi di file tersebut, lengkap dengan bukti testnya.
Pusat Registrasi: bootstrap/app.php #
Berikut isi nyata bootstrap/app.php dari aplikasi latihan kita, dengan semua bentuk registrasi sekaligus:
<?php
use App\Http\Middleware\FirstLayer;
use App\Http\Middleware\MarkGlobalRequest;
use App\Http\Middleware\SecondLayer;
use App\Http\Middleware\StampWebGroup;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
use Illuminate\Http\Request;
use Illuminate\Routing\Middleware\SubstituteBindings;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware): void {
$middleware->alias([
'token' => App\Http\Middleware\EnsureTokenIsValid::class,
'role' => App\Http\Middleware\EnsureRole::class,
]);
$middleware->append(MarkGlobalRequest::class);
$middleware->web(append: StampWebGroup::class);
$middleware->prependToPriorityList(SubstituteBindings::class, SecondLayer::class);
$middleware->prependToPriorityList(SecondLayer::class, FirstLayer::class);
})
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->shouldRenderJsonWhen(
fn (Request $request) => $request->is('api/*') || $request->expectsJson(),
);
})->create();Closure withMiddleware menerima objek Middleware yang menjadi remote control kita. Method $middleware->web(append: ...) menempelkan StampWebGroup di akhir group web; ada juga argumen prepend:, remove:, dan replace: untuk posisi lain, serta $middleware->api(...) untuk group api. Method group('nama', [...]) bahkan bisa membuat group baru dari nol untuk kebutuhan khusus seperti route khusus admin. Perhatikan pula withRouting: selain web, kita mendaftarkan file routes/api.php — file ini tidak ada pada skeleton bawaan, kita buat sendiri agar route API punya rumah, dan Laravel otomatis memberinya prefix /api beserta group api.
Satu file ini membaca seperti daftar isi seluruh kebijakan HTTP aplikasi. Pembaca baru bisa tahu dalam hitungan detik middleware apa yang aktif global, apa yang menempel group, dan siapa yang diberi alias — tanpa harus berburu ke file konfigurasi yang tersebar. Karena semua registrasi juga berupa kode, perubahan urutan maupun penambahan middleware baru tercatat rapi di versi kontrol dan ikut teruji bersama test aplikasi.
Alias: Nama Ringkas untuk Route #
Alias memetakan nama pendek ke nama class, sehingga route tidak perlu menulis path lengkap. Setelah alias token terdaftar, kedua deklarasi berikut identik:
Route::get('/tugas', fn () => 'Daftar tugas')->middleware(\App\Http\Middleware\EnsureTokenIsValid::class);
Route::get('/tugas-alias', fn () => 'Daftar tugas')->middleware('token');Feature test berikut memanggil route kedua dan membuktikan perilakunya sama dengan middleware aslinya:
public function test_alias_token_berperilaku_sama(): void
{
$response = $this->get('/tugas-alias?token=rahasia-budi');
$response->assertStatus(200);
echo 'alias_token: status=', $response->getStatusCode(),
', isi="', $response->getContent(), '"', PHP_EOL;
}Output yang Dihasilkan:
alias_token: status=200, isi="Daftar tugas"Menempel ke Group dan Menempel Global #
StampWebGroup hanya berlaku pada route yang masuk group web, sedangkan MarkGlobalRequest dipasang lewat $middleware->append() sehingga menempel pada setiap request tanpa kecuali. Test berikut membandingkan tiga route: health check /up (tanpa group), route web /, dan route api /api/laporan/9:
public function test_middleware_global_menempel_di_semua_route(): void
{
$up = $this->get('/up');
$web = $this->get('/');
$api = $this->get('/api/laporan/9');
echo 'global_di_health: ', $up->headers->get('X-Global-Mw') ?? '(tidak ada)', PHP_EOL;
echo 'global_di_web: ', $web->headers->get('X-Global-Mw') ?? '(tidak ada)', PHP_EOL;
echo 'global_di_api: ', $api->headers->get('X-Global-Mw') ?? '(tidak ada)', PHP_EOL;
}Output yang Dihasilkan:
global_di_health: aktif
global_di_web: aktif
global_di_api: aktif
web_group_di_web: aktif
web_group_di_api: (tidak ada)Global (X-Global-Mw) hadir di ketiga route, sedangkan group web (X-Web-Group) hanya di route web. Untuk posisi terdepan pada global list, gunakan $middleware->prepend() alih-alih append().
Mengatur Urutan Prioritas #
Laravel mengurutkan middleware group dan route berdasar daftar prioritas agar, misalnya, session selalu aktif sebelum pemeriksaan auth berjalan. Method $middleware->prependToPriorityList(Sebelum::class, Baru::class) menyisipkan middleware baru ke daftar itu tanpa menimpa daftar bawaan. Route berikut sengaja mendeklarasikan SecondLayer lebih dulu:
Route::get('/urutan', function () {
TraceLog::add('Controller: memproses');
return 'Cek urutan';
})->middleware([SecondLayer::class, FirstLayer::class]);Karena FirstLayer kita sisipkan di atas SecondLayer pada daftar prioritas, urutan deklarasi tidak berlaku lagi:
Tanpa baris prependToPriorityList:
trace: Lapisan 2: sebelum > Lapisan 1: sebelum > Controller: memproses > Lapisan 1: sesudah > Lapisan 2: sesudah
Dengan baris prependToPriorityList:
trace: Lapisan 1: sebelum > Lapisan 2: sebelum > Controller: memproses > Lapisan 2: sesudah > Lapisan 1: sesudahDengan alias, group, global, dan prioritas di satu file, kita kini punya tiga lingkup pemasangan middleware. Chapter berikutnya membedah ketiganya sekaligus: kapan sebuah middleware sebaiknya global, kapan menempel group, dan kapan cukup menempel route tertentu. Daftar lengkap method Middleware beserta contohnya tersedia di dokumentasi middleware
.