Laravel 5 Middleware Stack Decoded

In Version 4.1, Laravel included the support for the Middlewares: the independent filter classes implementing the HttpKernel’s HttpKernelInterface that can be placed on to the application’s request / response processing stack to alter the request / response, or, to introduce the additional functionality such as logging, session handling, caching etc. Though the feature was powerful, its usage was not officially emphasized and no documentation was provided. Recently, Taylor has mentioned that, the transition from Laravel 3 to 4.x was huge and there were a lot of things for the people to digest so the middlewares were not introduced as the first class citizens at that time. (ref: Podcast)

The middleware functionality in Laravel 4.1, was inspired by the project StackPHP and was adopted to provide the core middleware functionality to the framework. The StackPHP’s middleware implementation relies on the HttpKernelInterface, and introduces the convention that: by decorating an HttpKernelInterface instance, we can add as many layers as we want to the application’s request / response handling stack without modifying the actual application object.

Laravel 5 Middlewares And The Stack Implementation

In Laravel 5, using middlewares is the preferred way of hooking into the Http layer of the framework. They should be used instead of filters.

Now, the Laravel 5 has introduced its own implementation for the middleware stack through the Illuminate\Routing\Stack class and the Illuminate\Contracts\Routing\Middleware interface. The Illuminate\Foundation\Application class no longer implements the HttpKernelInterface, and the request handling functionality has been refactored into the new Illuminate\Foundation\Http\Kernel class. The Kernel class, now registers the application level middlewares, bootstraps the core components, builds the application’s middleware stack and handles the request by passing the current Request object down the stack. A diagram is shown below for the reference:

Laravel 5 Middlewares
Laravel 5 Middleware Stack

Application Level Middlewares Vs Route Level Middlewares

The middlewares in Laravel 5 can be applied on two levels, either on the application level or on the route level. The application level middlewares run for all the requests whereas, the route level middlewares run only for the requests matching a certain route.

In the above diagram, the prominent brown rectangles represent the application level middlewares, and the small brown rectangles inside the gray box (routing layer) represent the route level middleware stack.

When the framework boots up, the Application instance calls the handle method on the extended Illuminate\Foundation\Http\Kernel class provided in the App\Http namespace. The App\Http\Kernel class bootstraps the core and runs the stack by passing the current Illuminate\Http\Request object.

All the middleware classes in Laravel 5 should implement the Illuminate\Contracts\Routing\Middleware interface. This interface declares a single method i.e., handle($request, Closure $next). The handle method of the current middleware on the stack always receives a reference to a closure that contains a call to the handle method of the next middleware on the stack. This reference ($next) is used to pass the Request down to the next middleware on the stack.

Unlike filters, middlewares use a single signature for both the before and after implementations.

As you can see from the above diagram, when a response is returned it propagates upwards the stack. This means that in addition to the request, all the middlewares on the stack can also modify the response. This is the reason that no before or after term is attached to the middlewares. They are just The Middlewares — though you can differentiate according to their specific implementation. The following code elaborates the idea:

<?php namespace App\Http\Middleware;

use Closure;
use Illuminate\Contracts\Routing\Middleware;

class AllInOneMiddleware implements Middleware {

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        //before portion of the middleware
        //modify the request here or whatever
        //you want before handling the request.

        $response = $next($request); //boundary.

        //after portion of the middleware.
        //modify the response here or anything
        //you want to do before sending back the
        //response.

        return $response;
    }

}

As you can see from the above code, if you want to modify the request you can utilize the middleware portion above the boundary call. Similarly, for the response modifications, the after portion of the middleware can be utilized.

Quick Tips

  • The default location for the middlewares in Laravel 5 is app/Http/Middleware.
  • You can use the new artisan make:middleware command to create a middleware stub.
  • To add a global or application level middleware, we use the $middleware property of the App\Http\Kernel class.
  • Route level middlewares can be registered using the $middleware property of the App\Providers\RouteServiceProvider class.
  • To activate a middleware on a certain route action, we use the new @Middleware annotation inside the dockblock of a controller method (route action).
  • If you are reluctant to use the annotations, you can use the 'middleware'=>'middleware.name' key value pair in your get,post,delete and put method calls on the $router instance (just like filters).

That’s all for now, Regards!

2 thoughts on “Laravel 5 Middleware Stack Decoded”

  1. I haven’t found a decent source yet, but, have you figured out if there is a way to pass in a parameter to a custom Middleware? i.e. $this->middleware(‘permissions’, [‘requires’ => ‘editOwnPost’] ) type of thing?

    1. Hi, I don’t think middlewares support optional parameters (yet). It seems that ControllerDispatcher parses the options array only for the except and only keys. Also, the implementation for the middleware method differs from the beforeFilter and afterFilter methods i.e., Controller class does not parse the middleware options.

      Hmm, IMHO you can use a config file for this purpose, some thing along the lines:

      <?php
      //  File: config/route_perms.php
      //  contains the route.name permission mappings.
      //  Format: ['route.name' => 'permission']
      
      return [
          
          //permissions for Post CRUD controller.
          'post.edit'   => 'editOwnPost',
          'post.delete' => 'deleteOwnPost',
          'post.show'   => 'viewOthersPost',
          
          //more here
      ];

      And then in your PermissionsMiddleware:

      <?php namespace App\Http\Middleware;
      
      use Closure;
      use Illuminate\Contracts\Auth\Guard;
      use Illuminate\Contracts\Routing\Middleware;
      use Illuminate\Contracts\Routing\ResponseFactory;
      
      class PermissionsMiddleware implements Middleware {
      
          /**
           * The Guard implementation.
           *
           * @var Guard
           */
          protected $auth;
      
          /**
           * Create a new middleware instance.
           *
           * @param  Guard  $auth
           * @param  ResponseFactory  $response
           * @return void
           */
          public function __construct(Guard $auth,
                                      ResponseFactory $response)
          {
              $this->auth = $auth;
              $this->response = $response;
          }
          /**
           * Handle an incoming request.
           *
           * @param  \Illuminate\Http\Request  $request
           * @param  \Closure  $next
           * @return mixed
           */
          public function handle($request, Closure $next)
          {
              //getting route name;
              $routeName = $request->route()->getName();
              $routePermission = config('route_perms.' . $routeName);
      
              //compare permissions on this route with current user's permissions
              if( ! $this->auth->user()->hasPermission($routePermission))
              {
                  return $this->response->make('Access Denied',403);
              }
      
              return $next($request);
          }
      
      }

      I don’t know if this suits your needs. Best Regards!

Leave a Reply

Your email address will not be published. Required fields are marked *

Time limit is exhausted. Please reload CAPTCHA.