Understanding Laravel Middleware

HTTP Middlewares provide a convenient mechanism for filtering HTTP requests entering your application. Laravel, for example, has a middleware for verifying a user's authentication.

Some Middleware Use-Cases

These are some cases where I have had to resort to using middleware. There are many more cases where you would like to use a middleware.

  • Using a middleware to confirm the incoming route request API key. Assuming you are building an API.
  • Rate-limiting a service call.
  • Change the site language based on locale.
  • Enable site-wide maintenance.
  • Sniffing bot traffic.
  • Logging.

By the end of this article, you should be able to create a middleware, registers it and use it in your projects. We will be illustrating the creation till usage of middlewares by creating one of our own. Our middleware will enable maintenance either site-wide or on some routes.

Creating a Middleware

Thanks to artisan, creating middlewares in Laravel is easy. All we need do is open a terminal in the project root and run the following command.

// Replace <MiddlewareName> with the actual name of the middleware.
php artisan make:middleware <MiddlewareName>

This command creates our middleware class in app/Http/Middleware. To create our own middleware (which we will call DownForMaintenance, we can.

php artisan make:middleware DownForMaintenance

So we can now open our middleware class and add our logic to the middleware. Remember, our middleware handles site maintenance mode. We need to first import the HttpException first. At the top of the file, we can do this

use Symfony\Component\HttpKernel\Exception\HttpException;

In the handle method of our middleware, we can just do this.

public function handle($request, Closure $next)
{
    throw new HttpException(503);
}

By throwing this exception, Laravel knows to load the 503.blade.php file. This should contain the message for maintenance mode.

Registering a Middleware

Now that we've created a middleware, we need to let the application know the middleware exists. If you want a middleware to run on every request, go to app/Http/kernel.php and add the middleware FQN to Kernel class $middleware property.

protected $middleware = [
    ...
    \App\Http\Middleware\DownForMaintenance::class
];

By doing this, the user will see a message for every page they visit.

If you want the middleware to trigger on some routes, we can name the middleware and use that as a reference mechanism to add it to some routes. To name the middleware, while still in the app/Http/kernel.php, add the keyed property to the $routeMiddleware array. The array key is the name of the middleware, while the value should be the FQN of the middleware.

protected $routeMiddleware = [
    ...
    'down.for.maintenance' => \App\Http\Middleware\DownForMaintenance::class,
    ...
];

Attaching a Middleware to a Route

Take this route for example,

Route::get('posts/{something}', 'PostController@getPost');

the getPost method on the PostController class fires when the url matches posts/{something}.

We could add our down.for.maintenance middleware by changing the second parameter of Route::get to an array which contains a middleware property and closure which processes the route.

Route::get('posts/{something}', ['middleware' => 'grown.ups.only', function () {
    return "Only big boys/girls can see this.";
}]);

By doing this, only routes matching posts/{something} will show the down for maintenance error.

Another to add middleware to routes is to call a middleware method on the route definition. Like this.

Route::get('posts/{something}', function () {
    //
})->middleware(['first', 'second']);

Middleware Parameters

Passing parameters to middlewares is quite easy. Say, for example, our middleware validates the role of a user before allowing access to a page. We could also pass a list of allowed roles to the middleware.

To pass a parameter to our middleware, after the $request and Closure parameters, we then add our variables.

public function handle($request, Closure $next, $role)
{
    if (! $request->user()->hasRole($role)) {
        // Redirect...
    }

    return $next($request);
}

To pass the variable, when attaching our middleware to routes, we do this.

Route::put('post/{id}', ['middleware' => 'role:editor', function ($id) {
    //
}]);

Grouping Middlewares

At times, there might be a bunch of middlewares you apply to a couple of routes. It would be better if we could combine or group middlewares. This allows us to reuse that group of middlewares.

To group middlewares, we add a $middlewareGroups property (if it does not exist) to the Kernel class. This property takes a key-value pair array. The key represents the group name, and the value is an array of middleware FQNs. By default, Laravel provides a web, and api group.

protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
    ],

    'api' => [
        'throttle:60,1',
        'auth:api',
    ],
];

If we need to use this group in our application, we can then do this.

Route::group(['middleware' => ['web']], function () {
    //
});

Deferring Middlewares Response

If you noticed, throughout this article we perform an action then execute our middleware. But we could also execute our middleware first before returning a response.

public function handle($request, Closure $next)
{
    $response = $next($request);

    /**
    * Perform actions here
    */

    return $response;
}

By doing this, we first execute our middleware then we perform our action and return the response.

Conclusion

The use-cases mentioned above are just some few examples. You can create a middleware to do so much more than what I have listed above.

Samuel Oloruntoba

Self-proclaimed full-stack web developer and a quasi-academic. I work mostly on the backend (PHP and Node) with a recent enthusiasm for frontend development (React, SVG, HTML5 Canvas).