Middleware
Middleware adalah rangkaian bawang di sekitar controller-mu. Memahami arah alirannya menjelaskan kenapa ada kode yang jalan sebelum controller dan ada yang setelah respons terkirim.
Intisari
- Bentuknya satu method
handle($request, Closure $next). Kode sebelum$nextberjalan saat masuk, setelahnya saat keluar. - Sejak Laravel 11 middleware didaftarkan di
bootstrap/app.php, bukan diapp/Http/Kernel.phpyang sudah tidak ada. - Tiga cakupan: global, grup (
web/api), dan per-rute lewat alias. - Laravel 13 menambahkan atribut
#[Middleware]supaya bisa ditempel langsung di controller. terminate()berjalan setelah respons dikirim ke pengguna โ tempat yang tepat untuk pekerjaan ringan pasca-respons.
Bentuk dan arah aliran
// app/Http/Middleware/PastikanLangganganAktif.php
class PastikanLangganganAktif
{
public function handle(Request $request, Closure $next): Response
{
// โโ MASUK: berjalan sebelum controller
if (! $request->user()?->langgananAktif()) {
return redirect()->route('langganan.bayar'); // hentikan di sini
}
$response = $next($request); // โ controller (dan middleware lain) berjalan di baris ini
// โโ KELUAR: berjalan setelah controller, sebelum respons dikirim
$response->headers->set('X-Langganan', 'aktif');
return $response;
}
}
permintaan โ A masuk โ B masuk โ CONTROLLER โ B keluar โ A keluar โ respons
Urutan keluar adalah kebalikan urutan masuk. Middleware yang didaftarkan paling awal adalah yang paling luar: ia melihat permintaan lebih dulu dan menyentuh respons paling akhir.
Mendaftarkan โ tiga cakupan
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
// 1. Global โ setiap permintaan, tanpa kecuali
$middleware->append(PaksaHttps::class);
// 2. Per grup โ hanya rute web, atau hanya rute api
$middleware->web(append: [CatatKunjungan::class]);
$middleware->api(prepend: [PastikanJson::class]);
// 3. Alias โ dipakai per rute
$middleware->alias([
'langganan' => PastikanLangganganAktif::class,
]);
})
// routes/web.php
Route::get('/laporan', [LaporanController::class, 'index'])
->middleware(['auth', 'langganan']);
Sejak Laravel 13, middleware juga bisa dinyatakan langsung di controller lewat atribut:
use Illuminate\Routing\Attributes\Controllers\Middleware;
#[Middleware('auth')]
class LaporanController
{
#[Middleware('langganan')]
public function bulanan() { /* ... */ }
}
Kapan memilih yang mana. Aturan praktisnya: kalau syaratnya berlaku untuk seluruh aplikasi, pakai global; kalau berlaku untuk sekelompok rute yang sudah punya nama (admin, api), pakai grup; kalau berlaku untuk beberapa aksi tertentu, pakai atribut atau alias di rute. Middleware global yang sebenarnya hanya relevan untuk tiga rute adalah pemborosan yang terasa saat lalu lintas naik.
Middleware dengan parameter
public function handle(Request $request, Closure $next, string ...$peran): Response
{
if (! $request->user()->hasAnyRole($peran)) {
abort(403);
}
return $next($request);
}
Route::get('/admin', ...)->middleware('peran:admin,supervisor');
terminate() โ setelah pengguna menerima respons
class CatatDurasi
{
public function handle(Request $request, Closure $next): Response
{
return $next($request);
}
public function terminate(Request $request, Response $response): void
{
// Pengguna sudah menerima halamannya. Ini tidak menambah waktu tunggunya.
Log::info('durasi', [
'rute' => $request->route()?->getName(),
'status' => $response->getStatusCode(),
'ms' => round((microtime(true) - LARAVEL_START) * 1000),
]);
}
}
Peringatan untuk Fase 8. terminate() memang berjalan setelah respons terkirim, tapi di
worker mode ia tetap menahan worker tersebut sampai selesai. Di PHP-FPM, proses itu memang mau
mati; di worker mode, ia seharusnya sudah siap melayani permintaan berikutnya. Pekerjaan berat tetap harus masuk
antrean, bukan ke terminate().
Middleware bawaan yang perlu kamu kenal
| Alias | Gunanya |
|---|---|
auth | Wajib login; kalau tidak, lempar ke halaman login |
auth:sanctum | Wajib token API yang sah (Fase 4) |
verified | Wajib email terverifikasi |
throttle:60,1 | Maksimum 60 permintaan per menit (Fase 4) |
signed | URL bertanda tangan โ untuk tautan sekali pakai |
can:update,produk | Jalankan policy sebelum controller (Fase 4) |
Latihan: buat middleware CatatDurasi di atas, pasang sebagai global, lalu tambahkan
Log::info('masuk') sebelum $next dan Log::info('keluar') sesudahnya di dua
middleware berbeda. Muat satu halaman, lalu baca storage/logs/laravel.log dan pastikan urutannya
benar-benar seperti diagram bawang di atas.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.