Statamic Peak

Article

Laravel: Middlewares

Today we'll see a key part of all web projects : middlewares sometimes called filters. Easy to understand for senior developers, they are a real difficulty for beginners.

In this article, we introduce middlewares and their use into Laravel. I'm writing a compendium on the Laravel framework introducing its functionning. I notably explaining the requests life cycle et how are middlewares included into it.

What's a middleware ?

To make things simple, these are functions allowing to improve or check a request before it gets to your controller.

Many middlewares are included by default in Laravel, like auth allowing access to a route only for the requests linked to a logged in user.

Default Middlewares

Laravel includes the following middlewares in each new project :

  • Authenticate : return a 401 if the request doesn't contain an Authorization
  • CheckForMaintenanceMode : If the app is in maintenance mode, check if the IP or the request is allowed to execute despite the maintenance
  • EncryptCookies : Encrypt the cookies
  • RedirectIfAuthenticated : Redirect on the specified route like Home if the user is logged in
  • TrimStrings : Trim every field of the request body
  • TrustProxies : List all proxies that send requests to your server
  • VerifyCsrfToken : check the Csrf token given by the request

The framework itself contains many more:

  • AuthenticateWithBasicAuth
  • Authorize
  • EnsureEmailsVerified
  • RequiredPassword
  • AddQueuedCookiesToResponse
  • ConvertEmptyStringToNull
  • TransformRequest
  • ValidatePostSize
  • CheckResponseForModifications
  • FrameGuard
  • SetCacheHeadersd
  • SubstituteBindings
  • ThrottleRequests
  • ThrottleRequestsWithRedis
  • ValidateSignature
  • AuthenticateSession
  • StartSession
  • ShareErrorsFromSession

Create your own middleware

With artisan, it's possible to create your own middleware with the following command :

php artisan make:middleware CheckAge

For Lumen, you can directly create the file in app/Http/Middleware.

 <?php
namespace App\Http\Middleware;
use Closure;
class CheckAge { 
	/** 
    * Handle an incoming request. 
    * 
    * @param \Illuminate\Http\Request $request 
    * @param \Closure $next * @return mixed 
    */ 
    public function handle($request, Closure $next)
    {
    	if ($request->age <= 200) {
    		return redirect(‘home’); 
        }
	}
}

This class uses a handle function which will be call by the framework when you will use your middleware. The most important is the call the the callback next who allows to keep the request going.

It is possible to add parameters to the handle function that you can define when you call it.

<?php
namespace App\Http\Middleware;
use Closure;
class CheckRole { 
	/**
    * Handle the incoming request. 
    * 
    * @param \Illuminate\Http\Request $request 
    * @param \Closure $next 
    * @param string $role 
    * @return mixed 
    */ 
    public function handle($request, Closure $next, $role)
    {
    	if (! $request->user()->hasRole($role)) {
        // Redirect… 
        }
	}
}

Once your middleware is created, we just have to register it for it to be recognized by Laravel.

Register your middleware

Depending of your needs, there are many ways to register your middleware: - for the whole app - for a single route - in a middleware group.

Register your middleware for the whole app

For it to launch at each request, you need to register the $middleware  attribute into the app/Http/Kernel.php file.

Register a middleware for a single route

The middleware's list you can use for your routes or routes groupes is located in the $routeMiddleware attribute from the App\Http\Kernel class. It can be shaped like an associative array where the key is a string which will be used as an identifier ans the value is your middleware class.

Then we can use it directly into our routes.

Route::get('admin/profile', "MyController@fonction")->middleware('auth');

It is possible to use the class name instead of the array's key to benefit from the autocomplete.

Route::get('admin/profile', "MyController@fonction")->middleware(CheckAge::class);

It is also possible to register multiple middlewares for a single route.

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

Finally, it is possible to block a specific middleware execution from a group for one of its routes.

Route::middleware([CheckAge::class])->group(function () {
	Route::get(‘/’, function () {});
	Route::get('admin/profile', function () {})->withoutMiddleware([CheckAge::class]);
});

Register a middleware for a request group

When you use multiple middlewares together, it is possible to create a group into the $middlewareGroups property. You will then only need to call this group to use all its middlewares.

This is the default use of Laravel with the web and api groups.

/**
* The application's route middleware groups.
* @var array
*/
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,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],
	'api' => [
    	'throttle:60,1','auth:api',
    ],
];

Pass parameters to your middleware

You pass parameters to your middleware with a : after the middleware identifier. If there are many arguments, we separate them with ,.

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