Laravel - Middleware
Laravel - Middleware
Section titled “Laravel - Middleware”Middleware provides a convenient mechanism for inspecting and filtering HTTP requests entering your application. For example, Laravel includes a middleware that verifies the user of your application is authenticated. If the user is not authenticated, the middleware will redirect the user to the application’s login screen. However, if the user is authenticated, the middleware will allow the request to proceed further into the application.
Think of middleware as layers an HTTP request must pass through before it hits your application. Each layer can examine the request and even reject it entirely.
Define Middleware
Section titled “Define Middleware”Middleware acts as an intermediary between a request and a response. It’s a form of filtering mechanism. For example, Laravel’s built-in Authenticate middleware checks if a user is logged in. If they are, the request proceeds; otherwise, they’re redirected to the login page.
You can generate a new middleware using the Artisan command:
php artisan make:middleware <MiddlewareName>Replace <MiddlewareName> with the desired name for your middleware (e.g., CheckUserAge). The new middleware class will be placed in the app/Http/Middleware directory.
Example: Creating an Age Middleware
Section titled “Example: Creating an Age Middleware”Step 1: Generate the middleware. Let’s create an EnsureUserIsOldEnough middleware:
php artisan make:middleware EnsureUserIsOldEnoughStep 2: After running the command, Laravel will output a message indicating the middleware was created successfully:
INFO Middleware [app/Http/Middleware/EnsureUserIsOldEnough.php] created successfully.Step 3: The EnsureUserIsOldEnough.php file will be created in app/Http/Middleware. It will contain a handle method. Let’s modify it to check if a hypothetical ‘age’ parameter in the request is 18 or greater:
<?php
namespace App\Http\Middleware;
use Closure;use Illuminate\Http\Request;use Symfony\Component\HttpFoundation\Response;
class EnsureUserIsOldEnough{ /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next * @return \Symfony\Component\HttpFoundation\Response */ public function handle(Request $request, Closure $next): Response { // Example: Check for an 'age' input or a user's age attribute // This is a simplified example. In a real app, you'd fetch user's age from DB or session. if ($request->input('age', 0) < 18) { // Redirect to a different page or return an error response return redirect('home')->with('error', 'You must be 18 or older to access this page.'); }
return $next($request); }}Register Middleware
Section titled “Register Middleware”Before a middleware can be used, it must be registered in your application’s HTTP kernel. The HTTP kernel is located at app/Http/Kernel.php.
There are three main ways to register middleware:
- Global Middleware: These run on every HTTP request to your application. Register them in the
$middlewareproperty of yourapp/Http/Kernel.phpclass. - Middleware Groups: Group several middleware under a single key for easier assignment to routes. Laravel’s default
webandapigroups are defined in the$middlewareGroupsproperty. - Route Middleware (Aliases): Assign a short alias to a middleware, which can then be assigned to specific routes. Register these in the
$middlewareAliases(or$routeMiddlewarein older Laravel versions) property.
Registering Global Middleware:
Section titled “Registering Global Middleware:”To make a middleware run during every HTTP request, add it to the $middleware property:
protected $middleware = [ // ... other global middleware \App\Http\Middleware\EnsureUserIsOldEnough::class, // If you want it to be global];Registering Route Middleware (Aliases):
Section titled “Registering Route Middleware (Aliases):”For middleware that applies only to specific routes, assign it a key in the $middlewareAliases property. This key will be used to reference the middleware in your route definitions.
protected $middlewareAliases = [ 'auth' => \App\Http\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, // ... other aliases 'isOldEnough' => \App\Http\Middleware\EnsureUserIsOldEnough::class, // Our custom alias];Example: Registering EnsureUserIsOldEnough as Route Middleware
Section titled “Example: Registering EnsureUserIsOldEnough as Route Middleware”Let’s register the EnsureUserIsOldEnough middleware with an alias isOldEnough in app/Http/Kernel.php:
<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel{ // ... (other properties like $middleware, $middlewareGroups)
protected $middlewareAliases = [ 'auth' => \App\Http\Middleware\Authenticate::class, // ... other default aliases 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'isOldEnough' => \App\Http\Middleware\EnsureUserIsOldEnough::class, // Added our middleware alias // ... other aliases ];}Once registered, you can assign the isOldEnough middleware to a route in your routes/web.php file:
use App\Http\Controllers\ProfileController;
Route::get('/restricted-area', [ProfileController::class, 'show'])->middleware('isOldEnough');
// You can also apply it to a group of routes:Route::middleware(['isOldEnough'])->group(function () { Route::get('/another-restricted-page', function () { return 'Welcome to another restricted page!'; });});Middleware Parameters
Section titled “Middleware Parameters”Middleware can also receive additional parameters. For instance, if your application needs to verify a user’s role before allowing access to certain actions, a CheckRole middleware could accept the required role as a parameter.
To pass parameters, append them to the middleware name in the route definition, separated by a colon :. Multiple parameters are comma-delimited. These parameters will be passed to the handle method after the $next argument.
Example: Creating and Using a CheckRole Middleware
Section titled “Example: Creating and Using a CheckRole Middleware”Step 1: Create the CheckRole middleware:
php artisan make:middleware CheckRoleStep 2: Modify the handle method in app/Http/Middleware/CheckRole.php to accept a role parameter:
<?php
namespace App\Http\Middleware;
use Closure;use Illuminate\Http\Request;use Symfony\Component\HttpFoundation\Response;
class CheckRole{ /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @param string $role The role to check against. * @return \Symfony\Component\HttpFoundation\Response */ public function handle(Request $request, Closure $next, string $role): Response { // Assuming $request->user()->hasRole($role) is a method to check user's role // This is a placeholder for your actual role checking logic. if (! $request->user() || ! $request->user()->hasRole($role)) { // For demonstration, we'll just abort. In a real app, redirect or return a JSON error. abort(403, 'Unauthorized action. Required role: ' . $role); }
// For this tutorial's original output style: // echo "Required Role: ".$role."<br>";
return $next($request); }}Step 3: Register the CheckRole middleware in app/Http/Kernel.php with an alias, for example, role:
protected $middlewareAliases = [ // ... other aliases 'role' => \App\Http\Middleware\CheckRole::class,];Step 4: Create a simple controller, e.g., AdminController:
php artisan make:controller AdminControllerAdd a method to app/Http/Controllers/AdminController.php:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class AdminController extends Controller{ public function dashboard() { return "Welcome to the Admin Dashboard!"; }}Step 5: Define a route in routes/web.php using the middleware with a parameter:
// In routes/web.phpuse App\Http\Controllers\AdminController;
// This assumes you have authentication set up and a user model with a hasRole() method.// For testing without full auth, you might need to mock the user or adjust the middleware logic.Route::get('/admin/dashboard', [AdminController::class, 'dashboard']) ->middleware(['auth', 'role:editor']); // Requires 'auth' and 'role' middleware with 'editor' parameter.Step 6: To test this, you would navigate to /admin/dashboard. If the authenticated user has the ‘editor’ role, they’ll see the dashboard. Otherwise, they’ll get a 403 error (or whatever logic you implemented in CheckRole).
Common Pitfall: Ensure your CheckRole middleware runs after authentication middleware if it relies on $request->user().
Terminable Middleware
Section titled “Terminable Middleware”Sometimes a middleware may need to do some work after the response has been sent to the browser. For example, a session middleware might write session data to storage after the response is sent. To achieve this, define a terminate method on your middleware.
The terminate method receives both the request and the response. Terminable middleware should be registered as global middleware or within a middleware group.
Example: Creating a LogResponseSent Terminable Middleware
Section titled “Example: Creating a LogResponseSent Terminable Middleware”Step 1: Create the LogResponseSent middleware:
php artisan make:middleware LogResponseSentStep 2: Add handle and terminate methods to app/Http/Middleware/LogResponseSent.php:
<?php
namespace App\Http\Middleware;
use Closure;use Illuminate\Http\Request;use Illuminate\Support\Facades\Log;use Symfony\Component\HttpFoundation\Response;
class LogResponseSent{ public function handle(Request $request, Closure $next): Response { // Log::info('LogResponseSent: Handling request before sending to next middleware/controller.'); // For tutorial's original output style: // echo "Executing statements of handle method of LogResponseSent Middleware.<br>"; return $next($request); }
/** * Handle tasks after the response has been sent to the browser. * * @param \Illuminate\Http\Request $request * @param \Symfony\Component\HttpFoundation\Response $response * @return void */ public function terminate(Request $request, Response $response): void { Log::info('LogResponseSent: Response has been sent to the browser.', [ 'url' => $request->fullUrl(), 'status' => $response->getStatusCode() ]); // For tutorial's original output style: // echo "<br>Executing statements of terminate method of LogResponseSent Middleware."; }}Step 3: Register LogResponseSent as a global middleware in app/Http/Kernel.php (or in a group like web):
protected $middleware = [ // ... other global middleware \App\Http\Middleware\LogResponseSent::class,];Step 4: Create a controller, e.g., HomeController:
php artisan make:controller HomeControllerAdd an index method:
<?php
namespace App\Http\Controllers;
class HomeController extends Controller{ public function index() { // echo "<br>HomeController: Processing request.<br>"; return "Welcome Home!"; }}Step 5: Define a route in routes/web.php:
// In routes/web.phpuse App\Http\Controllers\HomeController;
Route::get('/home-terminable', [HomeController::class, 'index']);Step 6: When you visit http://your-app.test/home-terminable, the handle method’s logic runs first, then the controller, then the browser receives “Welcome Home!”, and finally the terminate method’s logic executes, writing to your log file (e.g., storage/logs/laravel.log).
To see the echoed output from the original tutorial’s style, you’d need to ensure your web server configuration (like Nginx with PHP-FPM) allows for output buffering and flushing that makes echo in terminate visible. However, logging is the standard use case.
Additional Resources:
Section titled “Additional Resources:”For more in-depth information, refer to the official Laravel documentation on Middleware: https://laravel.com/docs/middleware