BAB 6: Middleware
Filter HTTP request yang berjalan sebelum request sampai ke controller — cara kerja dan penggunaan middleware.
Di bab sebelumnya kita mendefinisikan rute untuk aplikasi catatan dan bahkan menyebutkan middleware saat membahas route groups. Kita menaruh 'auth' di sana tanpa benar-benar membongkar cara kerjanya. Sekarang saatnya membuka lapisan itu.
Middleware adalah kode yang berjalan di antara request masuk dan controller yang menanganinya. Setiap request melewati satu atau lebih middleware sebelum sampai ke tujuannya — dan setelah controller selesai, response yang kembali juga bisa diproses ulang oleh middleware yang sama. Posisinya persis seperti pos pemeriksaan: request bisa diloloskan, dimodifikasi, atau ditolak di sini sebelum menyentuh logika utama aplikasi.
Cara Kerja Middleware
Sebelum menulis kode, penting untuk memahami alur eksekusinya secara visual.
Setiap middleware mendapat giliran dua kali: sekali sebelum memanggil $next($request), dan sekali lagi saat response sudah terbentuk. Kode sebelum $next() berjalan saat request masuk; kode setelah $next() berjalan saat response kembali ke atas.
Membuat Middleware
Laravel menyediakan Artisan command untuk membuat middleware baru:
php artisan make:middleware PeriksaLangganan
Perintah ini membuat file app/Http/Middleware/PeriksaLangganan.php dengan struktur dasar:
<?php
// app/Http/Middleware/PeriksaLangganan.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class PeriksaLangganan
{
public function handle(Request $request, Closure $next): Response
{
return $next($request);
}
}
Method handle() menerima dua parameter: objek $request yang berisi semua informasi request masuk, dan $next yang merupakan closure untuk meneruskan request ke middleware berikutnya (atau ke controller jika tidak ada middleware lagi).
Middleware Sebelum Request
Kode yang berjalan sebelum request mencapai controller ditempatkan sebelum pemanggilan $next():
<?php
// app/Http/Middleware/PeriksaLangganan.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class PeriksaLangganan
{
public function handle(Request $request, Closure $next): Response
{
// Cek status langganan sebelum request diproses
if ($request->user() && !$request->user()->langgananAktif()) {
return redirect('/tagihan')->with('pesan', 'Perbarui langganan untuk melanjutkan.');
}
return $next($request);
}
}
Jika kondisi tidak terpenuhi, middleware mengembalikan response langsung tanpa memanggil $next() — request berhenti di sini dan tidak pernah sampai ke controller.
Middleware Setelah Response
Untuk kode yang perlu berjalan setelah controller selesai — misalnya logging atau memodifikasi response — taruh logikanya setelah $next():
<?php
// app/Http/Middleware/CatatWaktuProses.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class CatatWaktuProses
{
public function handle(Request $request, Closure $next): Response
{
$mulai = microtime(true);
$response = $next($request); // request diproses controller
$durasi = round((microtime(true) - $mulai) * 1000, 2);
// Tambahkan header ke response
$response->headers->set('X-Waktu-Proses', $durasi . 'ms');
return $response;
}
}
Middleware ini mengukur berapa lama request diproses dan menyematkan hasilnya ke header response. Ini berguna untuk monitoring performa tanpa mengubah logika controller sama sekali.
Mendaftarkan Middleware
Di Laravel 12, pendaftaran middleware dilakukan di bootstrap/app.php — berbeda dari versi sebelumnya yang menggunakan app/Http/Kernel.php. File ini adalah titik konfigurasi utama untuk seluruh middleware stack.
Middleware Global
Middleware global berjalan pada setiap request yang masuk ke aplikasi, tanpa perlu didaftarkan di rute manapun:
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
$middleware->append(CatatWaktuProses::class);
})
Gunakan append() untuk menambahkan di akhir stack, atau prepend() untuk menambahkan di awal — sebelum middleware bawaan Laravel berjalan.
Middleware global berjalan di semua request, termasuk request ke route statis dan file publik. Pastikan kode di dalamnya ringan dan tidak melakukan operasi yang mahal seperti query database berat.
Alias Middleware
Middleware alias memungkinkan kamu merujuk ke class dengan nama pendek saat menggunakan middleware di rute:
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'langganan' => PeriksaLangganan::class,
'catat' => CatatWaktuProses::class,
]);
})
Setelah alias didefinisikan, gunakan nama pendek di rute:
// routes/web.php
Route::get('/dashboard', [DashboardController::class, 'index'])
->middleware('langganan');
Laravel sudah menyediakan beberapa alias bawaan yang siap digunakan:
| Alias | Middleware | Fungsi |
|---|---|---|
auth | Authenticate | Memastikan user sudah login |
guest | RedirectIfAuthenticated | Redirect user yang sudah login |
verified | EnsureEmailIsVerified | Memastikan email sudah diverifikasi |
throttle | ThrottleRequests | Membatasi jumlah request |
signed | ValidateSignature | Memvalidasi signed URL |
Middleware Group
Grup middleware memungkinkan beberapa middleware diaplikasikan sekaligus dengan satu nama. Laravel sudah menyediakan dua grup default: web dan api.
Kamu bisa menambahkan middleware ke grup yang sudah ada:
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
$middleware->web(append: [
PeriksaLangganan::class,
]);
})
Atau membuat grup baru:
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
$middleware->group('premium', [
PeriksaLangganan::class,
CatatWaktuProses::class,
]);
})
Gunakan grup di rute seperti middleware biasa:
// routes/web.php
Route::middleware('premium')->group(function () {
Route::get('/fitur-eksklusif', [FiturController::class, 'index']);
Route::get('/laporan', [LaporanController::class, 'index']);
});
Middleware dengan Parameter
Middleware bisa menerima parameter tambahan untuk membuat logikanya lebih fleksibel. Parameter diteruskan sebagai argumen ketiga dan seterusnya di method handle():
<?php
// app/Http/Middleware/PeriksaPeran.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class PeriksaPeran
{
public function handle(Request $request, Closure $next, string $peran): Response
{
if (!$request->user() || $request->user()->peran !== $peran) {
abort(403, 'Akses ditolak.');
}
return $next($request);
}
}
Setelah didaftarkan sebagai alias peran, panggil dengan tanda titik dua diikuti nilai parameter:
// routes/web.php
Route::get('/admin/pengguna', [Admin\PenggunaController::class, 'index'])
->middleware('peran:admin');
Route::get('/moderasi', [ModerasiController::class, 'index'])
->middleware('peran:moderator');
Untuk beberapa parameter, pisahkan dengan koma: ->middleware('peran:admin,moderator').
Mengecualikan Rute dari Middleware
Ketika sebuah grup menggunakan middleware tertentu, kamu bisa mengecualikan rute spesifik menggunakan withoutMiddleware():
// routes/web.php
Route::middleware('langganan')->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index']);
Route::get('/fitur', [FiturController::class, 'index']);
// Halaman ini tidak perlu cek langganan
Route::get('/harga', [HargaController::class, 'index'])
->withoutMiddleware('langganan');
});
Terminable Middleware
Ada kasus di mana kamu perlu menjalankan logika setelah response sudah dikirim ke browser — bukan hanya setelah controller selesai. Gunakan terminable middleware dengan menambahkan method terminate():
<?php
// app/Http/Middleware/CatatAktivitas.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class CatatAktivitas
{
public function handle(Request $request, Closure $next): Response
{
return $next($request);
}
public function terminate(Request $request, Response $response): void
{
// Kode ini berjalan setelah response dikirim ke browser
// Aman untuk operasi yang tidak harus cepat: logging, analytics, cleanup
if ($request->user()) {
AktivitasLog::create([
'user_id' => $request->user()->id,
'url' => $request->fullUrl(),
'method' => $request->method(),
'status' => $response->getStatusCode(),
'user_agent' => $request->userAgent(),
'created_at' => now(),
]);
}
}
}
Method terminate() dipanggil secara otomatis setelah response dikirimkan. Pengguna sudah menerima halamannya, sementara server masih menyelesaikan pencatatan di background — ini membuat halaman terasa lebih cepat di sisi pengguna.
Untuk terminable middleware yang membutuhkan state bersama antara handle() dan terminate(), daftarkan sebagai singleton di service provider agar instance yang sama digunakan di kedua method.
Urutan Eksekusi Middleware
Urutan middleware penting ketika satu middleware bergantung pada hasil middleware lain. Misalnya, middleware yang mengecek izin harus berjalan setelah middleware autentikasi — karena izin baru bisa dicek setelah kita tahu siapa usernya.
Atur prioritas di bootstrap/app.php:
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
$middleware->priority([
\Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
\Illuminate\Cookie\Middleware\EncryptCookies::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\Auth\Middleware\Authenticate::class,
PeriksaLangganan::class, // harus setelah Authenticate
PeriksaPeran::class, // harus setelah PeriksaLangganan
]);
})
Middleware yang terdaftar di array priority akan selalu dieksekusi sesuai urutan tersebut, terlepas dari urutan pendaftarannya di rute.
Latihan
Coba kerjakan latihan berikut untuk menguji pemahaman kamu:
-
Middleware logging — Buat middleware
CatatRequestyang menyimpan method HTTP, URL, dan timestamp setiap request ke file log menggunakan facadeLogLaravel. Daftarkan sebagai middleware global. -
Middleware mode maintenance — Buat middleware
PeriksaModeMaintenanceyang membaca nilai dari config atau database untuk menentukan apakah aplikasi sedang maintenance. Jika iya, tampilkan halaman khusus503untuk semua user kecuali admin. Petunjuk: gunakan$request->user()dan cek peran sebelum memutuskan response. -
Middleware throttle kustom — Buat middleware yang membatasi maksimal 5 kali percobaan login dalam 10 menit per IP address menggunakan
RateLimiterfacade bawaan Laravel. Kembalikan response JSON dengan status 429 jika limit terlampaui.
Penutup Bab
Middleware adalah lapisan kontrol yang membuat aplikasi Laravel bisa membedakan siapa yang boleh masuk, apa yang boleh dilakukan, dan bagaimana response perlu dimodifikasi — semuanya terpisah dari logika controller. Dengan memahami perbedaan antara middleware global, route middleware, dan terminable middleware, kamu sudah punya alat yang cukup untuk membangun pipeline request yang solid.
Sejauh ini kita hanya menyentuh controller sebagai tujuan akhir request, tapi belum benar-benar membedahnya. Di bab selanjutnya, kita akan masuk ke dalam controller secara menyeluruh: cara mengorganisir logika, berbagai jenis controller yang tersedia, dan bagaimana controller berinteraksi dengan layer di bawahnya.