Laravel Middleware
A zombie developer does not have any functionality between page requests, whereas a Laravel developer leverages Middleware to keep their app more secure and flexible.
You can think of middleware as the gatekeeper for requests. Before our app allows a request it must first pass through the gatekeeper. If the gatekeeper grants the user access the app will move on to the next request; however, if the user is denied access then the gatekeeper will not allow them to pass.
Middleware
Middleware is an easy way to run functionality between HTTP requests. An example middleware might disallow users access to certain routes if they are not authenticated.
As an example let's run some functionality to check if our current user is a zombie or not. If the user is a zombie, they are not allowed to access the route. For the sake of simplicity we are going to hard-code a variable to be true(1) or false(0) if the user is a zombie. Check out the following route:
Route::get('arsenal', function(){
$is_zombie = rand(0, 1);
if($is_zombie){
return redirect('/home');
}
// If the user is not a zombie we can run any code below
});
So, in the code above we are saying that if the user is a zombie we want to redirect them to the homepage route. Now, if we wanted another route we could do the same thing here:
Route::get('armory', function(){
$is_zombie = rand(0, 1);
if($is_zombie){
return redirect('/home');
}
// If the user is not a zombie we can run any code below
});
And now, any user who is a zombie will not be allowed access to any of those routes. Let's say we wanted to create one more route... Uhhh... We have to do the same code again...
Instead of continually adding the same code to protect against a zombie user we could instead create a Middleware to handle this functionality for us. We can use our good friend artisan to create a new middleware file for us. Run the following in your command line:
$ php artisan make:middleware isNotZombie
And now you should see a new file inside of app\Http\Middleware\isNotZombie.php
, which has the following contents:
<?php
namespace App\Http\Middleware;
use Closure;
class isNotZombie
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
return $next($request);
}
}
In the code above you can see that we have a function called handle
, and inside this function is where we will add our functionality to check if the user is a zombie or not, so we'll go ahead and change the handle
function above to look like the following:
public function handle($request, Closure $next)
{
$is_zombie = rand(0, 1);
if($is_zombie){
return redirect('/home');
}
// If the user is not a zombie we can run any code below
return $next($request);
}
Above we are running the same functionality to make sure the user was not a zombie. In the code above if the user is a zombie they will be redirected to the homepage; however, if not, we just continue to the rest of the code.
The final step is to register our middleware in our app by giving it a specific name and adding our middleware class to our application Kernel located in app\Http\Kernel.php
.
We will need to add our class to the protected $routeMiddleware = []
array like so
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'zombie' => \App\Http\Middleware\isNotZombie::class,
];
Notice above there are already a few middleware routes that come packaged with Laravel. In fact, we'll be learning more about the 'auth' middleware in the next section.
Now, to run our middleware we can create a Middleware Group and add our routes inside:
Route::group(['middleware' => ['zombie']], function () {
Route::get('arsenal', function(){
// If the user is not a zombie we can run any code below
});
Route::get('armory', function(){
// If the user is not a zombie we can run any code below
});
});
This is great, now any routes that we want to protect against any user that is a zombie can be wrapped in the zombie middleware group.
Web Middleware
If you look inside the app\Http\Kernel.php
file, you will notice a middleware group that looks like the following:
protected $middlewareGroups = [
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' => [
'throttle:60,1',
'bindings',
],
];
];
As you can see we have a lot of middleware classes inside of our web
middleware array. All these middleware functions and classes are used before we hit any route we add to our routes\web.php
file. Laravel provides us these awesome middleware classes such as starting sessions, protecting from cross site requests, and cookie handling. These are provided out of the box with any routes that we add to our routes\web.php
.
Multiple Middleware
We can even nest groups inside of each other. The following will group our routes in the auth
and the zombie
middleware:
Route::group(['middleware' => ['auth']], function () {
//
Route::group(['middleware' => ['zombie']], function () {
// Add our routes here
});
});
Fantastic! But we could make this even more efficient if we want to include both middlewares in the same group:
Route::group(['middleware' => ['auth', 'zombie']], function () {
// Add our routes here
});
You can see that we can add an array of middleware classes in our group, and they will all be run before we access the specified routes within.
Route Specific Middleware
As an alternative to using Middleware Groups we can add Middleware to each route specifically, like so:
Route::get('arsenal', ['middleware' => 'zombie', function () {
//
}]);
We could also chain the middleware method to our route definition, like so:
Route::get('arsenal', function () {
//
})->middleware(['first', 'second']);
You can see there are multiple ways to add your middleware and it depends on the way that works best for you.
Controller Middleware
You could also specify Middleware in a controller.
The methods in that controller will always run the Middleware before running any method. Let's say that our routes above linked to a controller method, like so:
Route::get('arsenal', 'WeaponsController@arsenal');
Route::get('armory', 'WeaponsController@armory');
Our above routes will use methods from a WeaponsController. So let's create that 'WeaponsController' by running the following artisan command:
$ php artisan make:controller WeaponsController
And a new file will be created at app\Http\Controllers\WeaponsController.php
with the following contents:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
class WeaponsController extends Controller
{
//
}
Inside of our new class we can create a constructor, and easily specify any type of middleware like so:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
class WeaponsController extends Controller
{
//
public function __construct()
{
$this->middleware('zombie');
}
}
Any WeaponsController method will run through this Middleware before being executed. This kind of flexibility makes it easy to add functionality between any HTTP request of any controller.
We could always get by without using middleware in our projects, but after you get the hang of how to use them it will make your code more flexible, readable, and fun to work with.
Let's move on now to Authentication and you'll see another example of middleware and how we can protect certain routes from only being accessible to authenticated users.