DEV Community

Cover image for Laravel Middleware
Mike Varenek
Mike Varenek

Posted on

Laravel Middleware

Introduction

Middleware stands as a crucial component facilitating seamless communication and interaction between various layers of an application. It serves as a bridge, intercepting incoming requests, performing operations, and shaping responses before they reach their final destination. In this article, we delve into the realm of middleware within the context of Laravel, one of the most popular PHP frameworks renowned for its elegant syntax and powerful features.

Explanation of Middleware in Web Development
Middleware, in its essence, refers to software components that sit between different layers of an application, facilitating communication and data exchange. In web development, middleware intercepts HTTP requests and responses, allowing developers to perform various tasks such as authentication, authorization, logging, and more, before reaching the intended route handler or controller.

Middleware operates on the principle of the "chain of responsibility" design pattern, wherein each middleware component in the chain has the ability to either modify the request, pass it to the next middleware, or halt the request entirely. This modular approach not only promotes code reusability but also enables developers to compartmentalize concerns, making applications more maintainable and extensible.

Importance of Middleware in Laravel Framework

In Laravel, middleware plays an important role in shaping the flow of incoming HTTP requests and outgoing responses. Laravel's middleware layer sits between the incoming request and the application's route handlers, providing developers with a robust mechanism to inject custom logic at various stages of the request lifecycle.

One of the key strengths of Laravel middleware lies in its flexibility and versatility. Whether it's implementing authentication and authorization mechanisms, handling cross-site request forgery (CSRF) protection, logging requests, or performing custom operations, middleware empowers developers to tailor the request-handling process according to the specific requirements of their applications.

How Middleware Works in Laravel Framework

Here's a simplified explanation of how middleware functions in Laravel:

1) Request Arrives: An HTTP request enters your Laravel application.
2) Middleware Stack: The request encounters a stack of middleware defined within your application.
3) Middleware Execution: Each middleware in the stack is executed sequentially.
4) A middleware can perform actions like:
Accessing request data (headers, body, etc.)
Checking user authentication status
Modifying the request object
It has the option to:
Terminate the request by returning a response (e.g., redirecting to a login page if unauthenticated).
Pass the request along to the next middleware in the stack.
5) Controller Reached: If no middleware terminates the request, it eventually reaches the intended controller for processing.
6) Response Generated: The controller generates a response based on the request.
7) Middleware on Response: The response may also pass back through the middleware stack, allowing for further modifications if necessary.

Middleware Execution Flow

The order of middleware execution is crucial. You can define two types of middleware in Laravel:

Global Middleware: Registered in the app/Http/Kernel.php file, these middleware apply to every single request hitting your application.
Route Middleware: Assigned to specific routes within your application's route definitions. This allows you to apply middleware selectively based on the route being accessed.

The middleware stack processes requests in the following order:

  1. Global Middleware: All globally registered middleware execute sequentially.
  2. Route Middleware: Any middleware assigned to the specific route being accessed are processed in the order defined.
  3. Controller: The request reaches the designated controller for handling.
  4. Post-Response Middleware: After the controller generates a response, any middleware defined for the route (in reverse order) or globally can perform final modifications on the response.

Let's create a custom global middleware that logs each incoming request:

Step 1: Create a new middleware using the artisan command:

php artisan make:middleware LogHttpRequest
Enter fullscreen mode Exit fullscreen mode

Step 2: Open the generated middleware file App\Http\Middleware\LogHttpRequest.php and implement the logic to log incoming requests:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Log;

class LogHttpRequest
{
    public function handle($request, Closure $next)
    {
        // Log the incoming request
        Log::info('Incoming Request: ' . $request->fullUrl());

        return $next($request);
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Register the custom middleware in the $middleware array of App\Http\Kernel.php:

protected $middleware = [
    // Other middleware...
    \App\Http\Middleware\LogHttpRequest::class,
];
Enter fullscreen mode Exit fullscreen mode

With this setup, the LogHttpRequest middleware will be executed for every incoming request, logging the URL of each request.

Creating Custom Middleware

Creating custom middleware in Laravel is a straightforward process. Below is a step-by-step guide to creating custom middleware:

Step 1: Generate a new middleware using the Artisan command-line tool. Open your terminal and run the following command:

php artisan make:middleware CustomMiddleware
Enter fullscreen mode Exit fullscreen mode

This command will create a new middleware class file named CustomMiddleware.php in the app/Http/Middleware directory.

Step 2: Open the generated middleware file (CustomMiddleware.php) in your text editor. You'll find a handle method within this file. This method receives an incoming request and a closure. You can add your custom logic inside this method.

<?php

namespace App\Http\Middleware;

use Closure;

class CustomMiddleware
{
    public function handle($request, Closure $next)
    {
        // Add your custom logic here

        return $next($request);
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Implement your custom logic inside the handle method. This could include tasks such as authentication, authorization, logging, modifying the request/response, etc.

Step 4: If your middleware needs additional parameters, you can pass them as constructor arguments. For example:

public function __construct($parameter)
{
    $this->parameter = $parameter;
}
Enter fullscreen mode Exit fullscreen mode

Step 5: If necessary, you can also perform actions before or after the request passes through the middleware by using the before or after methods. These methods are called automatically by Laravel before and after the handle method, respectively.

Step 6: Once your custom middleware is ready, you can register it in the $routeMiddleware array in the App\Http\Kernel.php file.

Registering Middleware in Laravel Applications

Middleware in Laravel can be registered in two ways:

Global Middleware: These are registered in the $middleware property of the App\Http\Kernel class.

Route Middleware: These are registered in the $routeMiddleware property of the same class.

To register middleware, follow these steps:

Step 1: Open the App\Http\Kernel.php file in your Laravel application.

Step 2: Locate the $middleware property for global middleware or the $routeMiddleware property for route middleware.

Step 3: Add your middleware to the corresponding array with a key-value pair. The key will be used to reference the middleware in your routes or controllers.

protected $routeMiddleware = [
    'custom' => \App\Http\Middleware\CustomMiddleware::class,
];
Enter fullscreen mode Exit fullscreen mode

Configuring Middleware Parameters

If your middleware requires parameters, you can pass them when registering middleware in the App\Http\Kernel.php file. Here's how:

protected $routeMiddleware = [
    'custom' => \App\Http\Middleware\CustomMiddleware::class,
];
Enter fullscreen mode Exit fullscreen mode

Then, in your middleware constructor, you can accept these parameters:

public function __construct($parameter)
{
    $this->parameter = $parameter;
}
Enter fullscreen mode Exit fullscreen mode

When applying middleware to routes, you can pass parameters like this:

Route::get('/example', function () {
    //
})->middleware('custom:param1,param2');
Enter fullscreen mode Exit fullscreen mode

When applying middleware to routes, you can pass parameters like this:

Route::get('/example', function () {
    //
})->middleware('custom:param1,param2');
Enter fullscreen mode Exit fullscreen mode

In this example, param1 and param2 will be passed to the middleware's constructor as separate arguments.

Passing Parameters to Middleware

Passing parameters to middleware in Laravel can be achieved by appending them to the middleware name when applying the middleware to routes. Here's how you can pass parameters to middleware:

Define Parameters in Middleware Constructor: First, define parameters in the constructor of your middleware class.

<?php

namespace App\Http\Middleware;

use Closure;

class CustomMiddleware
{
    protected $parameter;

    public function __construct($parameter)
    {
        $this->parameter = $parameter;
    }

    public function handle($request, Closure $next)
    {
        // Middleware logic here
        return $next($request);
    }
}
Enter fullscreen mode Exit fullscreen mode

Pass Parameters When Applying Middleware: When applying middleware to routes, pass parameters separated by commas after the middleware name.

Route::get('/example', function () {
    //
})->middleware('custom:param1,param2');
Enter fullscreen mode Exit fullscreen mode

Access Parameters Within Middleware: Parameters passed to middleware can be accessed within the middleware's handle method.

public function handle($request, Closure $next)
{
    // Access parameters
    $param1 = $this->parameter[0];
    $param2 = $this->parameter[1];

    // Middleware logic here
    return $next($request);
}
Enter fullscreen mode Exit fullscreen mode

Middleware Priority and Execution Order

Middleware priority and execution order determine the sequence in which middleware is executed. In Laravel, middleware can be assigned priority through the $middlewarePriority property in the App\Http\Kernel class. The default priority is defined in the $middlewarePriority array, where the keys represent middleware aliases, and the values represent their priority levels.

Best Practices for Managing Middleware Priority

Managing middleware priority is essential for controlling the order of execution and ensuring that middleware functions as expected. Here are some best practices:

Understand Default Priority: Familiarize yourself with the default middleware priority defined in the $middlewarePriority property of the App\Http\Kernel class. Laravel executes middleware in the order specified in this array.

Review Middleware Dependencies: Before assigning priority to middleware, consider any dependencies or interactions between middleware. Ensure that middleware required for authentication, authorization, or request handling has higher priority.

Test Middleware Execution Order: Test the execution order of middleware to verify that the desired sequence is followed. Use debugging tools or log statements within middleware to track their execution flow.

Avoid Conflicts: Be cautious when assigning priorities to middleware to avoid conflicts or unintended consequences. Ensure that middleware with conflicting actions or behaviors are executed in the correct sequence.

Document Middleware Priority: Document the priority assigned to middleware in your application. Maintain a record of middleware dependencies and execution order to facilitate maintenance and troubleshooting.

By following these best practices, you can effectively manage middleware priority in your Laravel applications, ensuring seamless execution and consistent behavior.

Let's create a real-world example of a Laravel middleware for logging HTTP requests.
This middleware will log details such as the request method, URL, request parameters, and timestamp to a log file.

Step 1: Create the Middleware

Generate a new middleware using the Artisan command-line tool:

php artisan make:middleware LogHttpRequests
Enter fullscreen mode Exit fullscreen mode

This will create a new middleware file named LogHttpRequests.php in the app/Http/Middleware directory.

Step 2: Implement the Middleware

Open the LogHttpRequests.php file and implement the logic to log HTTP requests:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Log;

class LogHttpRequests
{
    public function handle($request, Closure $next)
    {
        // Log the incoming request
        Log::info('HTTP Request Logged:', [
            'method' => $request->method(),
            'url' => $request->fullUrl(),
            'parameters' => $request->all(),
            'timestamp' => now()->toDateTimeString(),
        ]);

        return $next($request);
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Register the Middleware

Add the middleware to the $middleware array in the App\Http\Kernel class to make it a global middleware:

protected $middleware = [
    // Other middleware...
    \App\Http\Middleware\LogHttpRequests::class,
];
Enter fullscreen mode Exit fullscreen mode

Step 4: Use the Middleware

The middleware is now registered globally and will log all incoming HTTP requests. You can also apply it selectively to routes by specifying it in the route definition:

Route::get('/example', function () {
    //
})->middleware(\App\Http\Middleware\LogHttpRequests::class);
Enter fullscreen mode Exit fullscreen mode

Step 5: Test the Middleware

Make HTTP requests to your Laravel application, and you should see log entries in the configured log file (usually storage/logs/laravel.log). These log entries will contain details about each incoming HTTP request, including the request method, URL, parameters, and timestamp.

This example demonstrates how to create a Laravel middleware for logging HTTP requests, providing valuable insight into the incoming traffic to your application.

Middleware Groups and Middleware Stacks in Laravel

Laravel provides two powerful mechanisms for organizing and managing middleware: middleware groups and middleware stacks.

1. Creating Middleware Groups for Route Groups
Middleware groups allow you to define a collection of middleware that can be applied to multiple routes with a single reference. This promotes code reuse and simplifies route definitions.

Here's how to create and use middleware groups:

Define the Group:

In app/Http/Kernel.php, add a new key-value pair to the $middlewareGroups property:

protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \Illuminate\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],
    // Define other groups as needed
];
Enter fullscreen mode Exit fullscreen mode

Assign the Group to Routes:

Within your route definitions, use the middleware method with the group name:

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

Route::group(['middleware' => 'web'], function () {
    // All routes within this group will use the 'web' middleware group
    Route::get('/profile', function () {
        // ...
    });
    Route::get('/dashboard', function () {
        // ...
    });
});
Enter fullscreen mode Exit fullscreen mode

2.Implementing Middleware Stacks for Complex Applications
For more intricate scenarios, Laravel supports middleware stacks. These allow you to define a temporary middleware stack specifically for a route or controller action. This provides greater flexibility in handling complex request flows.

Here's how to use middleware stacks:

Define the Stack:

Use the withMiddleware method on the route definition:

Route::get('/admin', function () {
    // ...
})->withMiddleware(function ($middleware) {
    $middleware->push(\App\Http\Middleware\IsAdmin::class); // Add custom middleware
})->middleware('auth'); // Apply existing 'auth' middleware as well
Enter fullscreen mode Exit fullscreen mode

The closure passed to withMiddleware receives the current middleware stack as an argument. You can then use methods like push or prepend to add or insert custom middleware into the stack for that specific route.

Examples of Using Middleware Groups and Stacks Effectively
Here are some practical examples of using middleware groups and stacks:

'web' Group: The default web group in Laravel applies essential middleware for web applications (session, CSRF protection, etc.) to a broad range of routes.
'api' Group: You can create an api group containing middleware specific to API requests, such as rate limiting or authentication checks.
Admin Panel: Use a middleware stack to add an IsAdmin middleware to specific routes within your admin panel, restricting access only to authorized users.

Built-in Middleware in Laravel

Laravel offers a variety of pre-built middleware that handle common tasks within web applications. These middleware streamline development by providing essential functionalities you can leverage directly.

Overview of Commonly Used Built-in Middleware
Here's a breakdown of some frequently used built-in middleware:

\App\Http\Middleware\EncryptCookies: Encrypts cookies for added security.
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse: Adds queued cookies to the outgoing HTTP response.
\Illuminate\Session\Middleware\StartSession: Starts a session for the request if one doesn't already exist.
\Illuminate\View\Middleware\ShareErrorsFromSession: Shares error messages from the session with the view.
\Illuminate\Http\Middleware\VerifyCsrfToken: Protects against Cross-Site Request Forgery (CSRF) attacks.
\Illuminate\Routing\Middleware\SubstituteBindings: Binds route model parameters to their corresponding Eloquent models.
These middleware are typically included in the default web group defined in app/Http/Kernel.php, ensuring they apply to most web application routes.

For reference, here's how you could manually apply some of these middleware to a route:

Route::get('/profile', function () {
    // This route uses the built-in session handling middleware
})->middleware(\Illuminate\Session\Middleware\StartSession::class);

Route::post('/form', function () {
    // This route enforces CSRF protection
})->middleware(\Illuminate\Http\Middleware\VerifyCsrfToken::class);
Enter fullscreen mode Exit fullscreen mode

Overview of Commonly Used Built-in Middleware

\App\Http\Middleware\EncryptCookies: Encrypts cookies for security.
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse: Adds queued cookies to the response.
\Illuminate\Session\Middleware\StartSession: Starts a session if one doesn't exist.
\Illuminate\View\Middleware\ShareErrorsFromSession: Shares session errors with the view.
\Illuminate\Http\Middleware\VerifyCsrfToken: Protects against CSRF attacks in POST requests.
\Illuminate\Routing\Middleware\SubstituteBindings: Binds route model parameters to Eloquent models.
These middleware are usually included in the web group (app/Http/Kernel.php), applying to most web routes.

More info

Laravel Middleware
Laravel Middleware: Enhancing Your Application’s Security and Functionality
Working With Controllers and Middleware in Laravel

Top comments (0)