Within Slim 3’s Request object, there’s a method called getIp() which is determines the client’s IP address. However it’s rather simplistic and potentially risky as it checks the X-Forwarded-For header with no ability to ignore this header or whitelist whether we trust the final proxy in the chain.

Determining the client’s IP address is an ideal use-case for middleware as we can inspect the headers in the request and then set an attribute so that middleware further down the chain can use it. Following my rather unimaginative naming, I’ve called it rka-ip-address-middleware.

However, in this article, I want to look at how easy it is to write useful PSR-7 middleware.

Minimal valid middleware

Writing middleware is really easy, so lets look at the basics of how I built this. You simply need to provide a callable that has this function signature:

function(RequestInterface $request, ResponseInterface $response, callable $next) : ReponseInterface

We are given a request and response object along with the next middleware in the chain and must return a response. Our middleware function must also be a callable itself and this is easy to do with a class by using the __invoke() magic method.

A minimal class that acts as PSR-7 middleware therefore looks something like this:

namespace RKAMiddleware;

use PsrHttpMessageServerRequestInterface;
use PsrHttpMessageResponseInterface;

class IpAddress
{
    public function __invoke(
        ServerRequestInterface $request,
        ResponseInterface $response,
        callable $next = null
    ) {
        if ($next) {
            $response = $next($request, $response);
        }
        return $response;
    }
}

This middleware doesn’t actually do anything by itself, but does do the minimum required:

  1. Call the $next middleware in the chain
  2. Return a $response object

Doing something interesting

For a piece of middleware to be useful, it has to do something. Generally, this means that we should look at the $request or $response objects that we have received and do something with them. For this example, we need to look at the request’s headers to determine the client’s IP address and then store it back into the request for use by subsequent middleware.

Obviously, if there is no subsequent middleware to call, then we don’t need to do this work anyway as there’s nobody to use it, so our code becomes:

    public function __invoke(
        ServerRequestInterface $request,
        ResponseInterface $response,
        callable $next = null
    ) {
        if (!$next) {
            return $response;
        }

        $ipAddress = $this->determineClientIpAddress($request);

        return $next($request, $response);
    }

(The implementation details of determineClientIpAddress are irrelevant for this discussion.)

We have to store the $ipAddress back into the $request object. The way to do this is to use the server attributes which exists to provide messaging between the middleware in the chain.

We’ll call our new attribute “ip_address” and as PSR-7 messages are immutable, we use the withAttribute() method to create a new Request object:

$request = $request->withAttribute('ip_address', $ipAddress);

So our middleware handler is complete:

    public function __invoke(
        ServerRequestInterface $request,
        ResponseInterface $response,
        callable $next = null
    ) {
        if (!$next) {
            return $response;
        }

        $ipAddress = $this->determineClientIpAddress($request);
        $request = $request->withAttribute('ip_address', $ipAddress);

        return $next($request, $response);
    }

Configuring middleware

One nice thing about using a class for your middleware is that it’s easy to configure. Let’s say that we want the user to be able to pick the name of the attribute to store the IP address into. We can do this quite easily by introducing a property and a constructor:

class IpAddress
{
    protected $attribName = 'ip_address';

    public function __construct($attribName = null)
    {
        if (!empty($attribName)) {
            $this->attribName = $attribName;
        }
    }

    public function __invoke(
        ServerRequestInterface $request,
        ResponseInterface $response,
        callable $next = null
    ) {
        if (!$next) {
            return $response;
        }

        $ipAddress = $this->determineClientIpAddress($request);
        $request = $request->withAttribute($this->attribName, $ipAddress);

        return $next($request, $response);
    }
}

Adding middleware to your application

There are a number of PSR-7 middleware-aware frameworks, that have different method names for adding middleware:

  • Slim 3: $app->add(new RKAMiddlewareIpAddress('client_ip'));
  • Radar: $adr->middle(new RKAMiddlewareIpAddress('client_ip'));
  • Expressive: $app->pipe(new RKAMiddlewareIpAddress('client_ip'));

To sum up

Middleware is a powerful way to inspect and act upon HTTP messages in your application. The PSR-7 standard is feature rich enough that with the attributes in ServerRequestInterface it provides a way to add additional information that can be used by subsequent middleware which is very useful and allows for a lot of flexibility in building your applications.

Source: AKRABAT

By Rob