DEV Community

Cover image for Complete Explanation With Example On Laravel Middleware
David Nguyen
David Nguyen

Posted on

Complete Explanation With Example On Laravel Middleware

In this tutorial, we will learn Laravel middleware. We will learn with custom middleware and also Laravel predefined middleware. Middleware is a very important part of every framework not only Laravel. But what is middleware and how this middleware works in every framework like Laravel?

What is middleware in Laravel?

Middleware provides an easy mechanism to inspect and filter HTTP requests before getting the required information from the request. There are lots of predefined middleware in Laravel. All the middleware in Laravel is registered in the app\Http\Kernel.php file. If you visit this path, you will see the following file is already registered.

<?PHP
/* App\Http\Kernel.php */

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    /**
     * The application's global HTTP middleware stack.
     * These middleware are run during every request to your application.
     */
    protected $middleware = [
        // \App\Http\Middleware\TrustHosts::class,
        \App\Http\Middleware\TrustProxies::class,
        \Fruitcake\Cors\HandleCors::class,
        \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    ];

    /**
     * The application's route middleware groups.
     */
    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            // \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
    ];

    /**
     * The application's route middleware.
     *
     * These middleware may be assigned to groups or used individually.
     *
     */
    protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
        'prevent-back-history' => \App\Http\Middleware\PreventBackHistory::class,
        'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
        'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
        'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class,
    ];
}
Enter fullscreen mode Exit fullscreen mode

Look at that Kernel.php class, there are three middleware arrays like $middleware, $middlewareGroups and $routeMiddleware.

  • $middleware: These middleware are run during every request to your application. Actually global middleware.
  • $middlewareGroups: The application's route middleware groups.
  • $routeMiddleware: This middleware may be assigned to groups or used individually. We can use it for specific routes only.

Why do we use middleware?

Take an example, If the user is not logged in, the middleware will redirect the user to your application's login screen. and, if the user is logged in, then the middleware will allow the request to proceed further into the application. How we can implement that? Check out your $routeMiddleware group, there is an auth middleware. We can use it to fulfill this requirement.

<?php
/* App\Http\Kernel.php */
protected $routeMiddleware = [
  'auth' => \App\Http\Middleware\Authenticate::class,
];
Enter fullscreen mode Exit fullscreen mode

Now if we use this auth middleware in our route like:

/* routes/web.php */
Route::get('/profile', function () {
    //
})->middleware('auth');
Enter fullscreen mode Exit fullscreen mode

Now only a logged-in user can visit this /profile URI. For this type of situation, we need a middleware and we can solve this situation using middleware.

Creating Custom Middleware in Laravel

To create a new middleware, there is an artisan command for creating custom middleware using the make:middleware Artisan command:

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

If you run this command will place a new EnsureTokenIsValid class within your app/Http/Middleware directory.

<?php
/* app/Http/Middleware/EnsureTokenIsValid.php */

namespace App\Http\Middleware;

use Closure;

class EnsureTokenIsValid
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        return $next($request);
    }
}
Enter fullscreen mode Exit fullscreen mode

Now make it useable in our application. Let's update it like below:

<?php
 /* app/Http/Middleware/EnsureTokenIsValid.php */

namespace App\Http\Middleware;

use Closure;

class EnsureTokenIsValid
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if ($request->input('token') !== 'my-secret-token') {
            return redirect('home');
        }

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

Now we can use this middleware in our application to filter user requests like:

<?php
/* routes/web.php */

use App\Http\Middleware\EnsureTokenIsValid;

Route::get('/profile', function () {
    //
})->middleware(EnsureTokenIsValid::class);
Enter fullscreen mode Exit fullscreen mode

Now you can visit this /profile URL only if you can provide a matching token, otherwise, you can not visit this page.

Registering Middleware

If we would like to assign middleware to specific routes, we should first assign the middleware inside $routeMiddleware arrays with the key name like:

<?php
/* App\Http\Kernel.php */

protected $routeMiddleware = [
    'check-token' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
];
Enter fullscreen mode Exit fullscreen mode

Now we can handle the previous route with middleware in the following way:

<?php
/* routes/web.php */

use App\Http\Middleware\EnsureTokenIsValid;

Route::get('/profile', function () {
    //
})->middleware('check-token');
Enter fullscreen mode Exit fullscreen mode

Assign Multiple Middleware to Route

We can assign multiple middlewares to route also. If we want to assign multiple middlewares to the route by passing an array of middleware names to the middleware method:

/* routes/web.php */

Route::get('/profile', function () {
    //
})->middleware(['auth', 'check-token']);
Enter fullscreen mode Exit fullscreen mode

Excluding Middleware From Route

When we assign middleware to a group of routes, we may also need to prevent the middleware from being applied to an individual route within the group. We may accomplish this using the withoutMiddleware method:

<?php
/* routes/web.php */

use App\Http\Middleware\EnsureTokenIsValid;

Route::middleware([EnsureTokenIsValid::class])->group(function () {
    Route::get('/', function () {
        //
    });

    Route::get('/profile', function () {
        //
    })->withoutMiddleware([EnsureTokenIsValid::class]);
});

//or

Route::middleware(['check-token'])->group(function () {
    Route::get('/', function () {
        //
    });

    Route::get('/profile', function () {
        //
    })->withoutMiddleware(['check-token']);
});
Enter fullscreen mode Exit fullscreen mode

We can also exclude a given set of middleware from an entire group of route definitions:

<?php
/* routes/web.php */

use App\Http\Middleware\EnsureTokenIsValid;

Route::withoutMiddleware([EnsureTokenIsValid::class])->group(function () {
    Route::get('/profile', function () {
        //
    });
});
Enter fullscreen mode Exit fullscreen mode

Middleware Parameters

We can pass parameters in Laravel middleware also. The Laravel middleware can also receive additional parameters. Additional middleware parameters can be passed to the middleware after the $next argument like:

<?php
 * app\Http\Middleware\EnsureUserHasRole.php */
namespace App\Http\Middleware;

use Closure;

class EnsureUserHasRole
{
    /**
     * 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...
        }

        return $next($request);
    }

}
Enter fullscreen mode Exit fullscreen mode

Middleware parameters must be specified when defining the route by separating the middleware name and parameters with a : and if Multiple then parameters should be delimited by commas:

/* routes/web.php */

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

If you want to learn more about Laravel middleware and how middleware works, then you can enroll in this course to enlarge your knowledge about middleware:

Terminable Middleware in Laravel

Suppose, we need to do something when the HTTP response has been sent to the browser. In this case, we can use terminable middleware. To do so, we have to add terminate method on your middleware like:

<?php
 /* app\Http\Middleware\TerminatingMiddleware.php */
namespace Illuminate\Session\Middleware;

use Closure;

class TerminatingMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        return $next($request);
    }

    /**
     * Handle tasks after the response has been sent to the browser.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Illuminate\Http\Response  $response
     * @return void
     */
    public function terminate($request, $response)
    {
        // Handle tasks after the response has been sent to the browser.
    }
}
Enter fullscreen mode Exit fullscreen mode

Look at the terminate method, It receives both the request and the response. Once we have defined a terminable middleware, we should add it to the list of routes or global middleware in the app/Http/Kernel.php file.

Source: laravelia

Top comments (0)