Middleware

Lumberjack supports PSR-15/7 Middleware. Middleware can be added via the Router to individual routes or route groups or via WordPress Controllers.

Adding Middleware to a route

At it's simplest, adding Middleware to a route can be done by passing an object to the middleware() function:

$middleware = new AddHeaderMiddleware('X-Key1', 'abc');

Router::get('route/uri', '\MyNamespace\TestController@testMethod')->middleware($middleware);

Multiple middleware can be added by passing more params to the middleware() function:

$header = new AddHeaderMiddleware('X-Key1', 'abc');
$auth = new AuthMiddleware();

Router::get('route/uri', '\MyNamespace\TestController@testMethod')->middleware($header, $auth);

Or alternatively, you can also pass an array of middleware:

$header = new AddHeaderMiddleware('X-Key1', 'abc');
$auth = new AuthMiddleware();

Router::get('route/uri', '\MyNamespace\TestController@testMethod')->middleware([$header, $auth]);

Adding Middleware to a group

Middleware can also be added to a group. To do so you need to pass an array as the first parameter of the group()function instead of a string.

$header = new AddHeaderMiddleware('X-Key1', 'abc');

Router::group(['prefix' => 'my-prefix', 'middleware' => $header]), function ($group) {
    $group->get('route1', function () {}); // GET `/my-prefix/route1`
    $group->get('route2', function () {}); // GET `/my-prefix/route2`
});

You can also pass an array of middleware if you need more than one:

$header = new AddHeaderMiddleware('X-Key1', 'abc');
$auth = new AuthMiddleware();

Router::group(['prefix' => 'my-prefix', 'middleware' => [$header, $auth]]), function ($group) {
    $group->get('route1', function () {}); // GET `/my-prefix/route1`
    $group->:get('route2', function () {}); // GET `/my-prefix/route2`
});

Defining Middleware on Controllers

You can also apply Middleware on a Controller class too, either for use with the Router or as a WordPress Controller. In order to do this your Controller must extend the App\Http\Controllers\Controller base class.

Middleware is added by calling the middleware() function in your Controller's __constructor().

use App\Http\Controllers\Controller;

class MyController extends Controller
{
    public function __construct()
    {
        // Add one at a time
        $this->middleware(new AddHeaderMiddleware('X-Key1', 'abc'));
        $this->middleware(new AuthMiddleware());

        // Add multiple with one method call
        $this->middleware([
            new AddHeaderMiddleware('X-Key1', 'abc',
            new AuthMiddleware(),
        ]);
    }
}

By default all Middleware added via a Controller will affect all methods on that class. To limit what methods Middleware applies to you can use only() and except():

use App\Http\Controllers\Controller;

class MyController extends Controller
{
    public function __construct()
    {
        // Only apply to `send()` method
        $this->middleware(new AddHeaderMiddleware('X-Key1', 'abc'))->only('send');

        // Apply to all methods except `show()` method
        $this->middleware(new AuthMiddleware())->except('show');

        // Multiple methods can be provided in an array to both methods
        $this->middleware(new AuthMiddleware())->except(['send', 'show']);
    }
}

Middleware Aliases

Available in v4.3.0 and above

Creating new instances of middleware in your routes or controllers can have a couple of negative outcomes:

  1. The objects are instantiated and take up memory whether they're used or not

  2. Your route definitions can become less readable

These issues can both be addressed using Middleware Aliases. Instead of creating instances in your code, you register a string alias that can be used instead.

Registering a Middleware Alias

To register an alias you can use the MiddlewareAliases facade. It is recommended that you use this in the boot() method of the AppServiceProvider class.

There are 3 ways to define your middleware alias:

  • A closure factory (recommended - always creates a new instance when used)

  • A class name (always resolves a new instance from the Container when used)

  • A previously instantiated object (you don't get the benefits of lazy loading)

See "Setting entries in the container" for more information about the differences between these. While only the class name uses the container, principally they behave in the same way with regards to lazy-loading and object instantiation.

It is recommended that the alias is registered using a closure that acts as a factory, like so:

namespace App\Providers;

use Rareloop\Lumberjack\Facades\MiddlewareAliases;

class AppServiceProvider
{
    function boot()
    {
        // Create from closure factory
        MiddlewareAliases::set('auth', function() {
            return new AuthMiddleware;
        });
    }
}

Using a class name

// Create from class name
MiddlewareAliases::set('cors', \My\Middleware\Cors::class);

// Or
MiddlewareAliases::set('cors', '\My\Middleware\Cors');

Using a previously instantiated object

When using this method, you do not get the benefits of lazy loading. i.e. The object will be created whether or not it is needed for the current request.

// Create from existing object
MiddlewareAliases::set('addheader', new \My\Middleware\AddHeader());

Using a Middleware Alias

Middleware Aliases can be used anywhere middleware can normally be used, both in the Router and through Controllers.

If, for example, an Alias had been registered for an AuthMiddleware with the key auth like so:

MiddlewareAliases::set('auth', function() {
    return new AuthMiddleware;
});

You could use this in your route definition like this:

Router::get(
    'route/uri', 
    '\App\Http\Controllers\TestController@testMethod'
)->middleware('auth');

Middleware Alias Parameters

You can register Middleware Aliases which accept parameters. It is advised if you're doing this to use the Closure Factory registration technique.

// Register in AppServiceProvider::boot()
MiddlewareAliases::set('addheader', function($key, $value) {
    return new AddHeader($key, $value);
});

// Use in routes.php
Router::get(
    'hello-world', 
    'MyController@hello'
)->middleware('addheader:X-Key:HeaderValue');

In this example, $key will have the value X-Key and $value will have the value of HeaderValue.

Creating custom Middleware

While you can use any PSR 15/7 Middleware, sometimes you need to write your own. In this example, we will create custom middleware that adds the response header X-Foo. First, create the class in app/Http/Middleware/ExampleMiddleware.php (create the folder if it doesn't exist) with the following content:

<?php

namespace App\Http\Middleware;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

class ExampleMiddleware implements MiddlewareInterface
{
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $response = $handler->handle($request);

        return $response;
    }
}

In our example, we want to modify the response. Below we add the X-Foo header to the response:

return $response->withHeader('X-Foo', 'Bar');

If you need to modify the request before it gets passed to your application, you can do so before $handler->handle($request) gets called:

public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
    // Chance to modify the request here before passing it into your application

    // Pass the request on (to the next middleware or your application)
    $response = $handler->handle($request);

    // Chance to modify the response here before sending it back

    return $response;
}

Last updated