Middleware all the things

Image result for middleware psr 15

The middleware pattern is really awesome, you have layers of midlleware classes that take a request and return a reponse, but pass to a handler first, eventually reaching the original callable your request routed to.

My framework Bone MVC makes use of PSR-7 requests, so I’ve been refactoring for v3.0.0 to use league/route and it’s middleware stack, and I’ve just realised a great use case, so I’m about to refactor and create a middleware!

Take a look at this controller action:

/**
* @param ServerRequestInterface $request
* @param array $args
* @return ResponseInterface
* @throws \Doctrine\ORM\EntityNotFoundException
*/
public function viewAction(ServerRequestInterface $request, array $args): ResponseInterface
{
$dragon = $this->service->getRepository()->find($args['id']);
$server = $request->getServerParams();
$hal = [
'_links' => [
'self' => [
'href' => $server['REQUEST_SCHEME'] . '://' . $server['SERVER_NAME'] . '/api/dragon/' . $args['id']
]
],
];
$payload = array_merge($hal, $dragon->toArray());

return new JsonResponse($payload);
}

As well as returning the entity in array format to the JSON response, I’m also creating HAL content negotiation. I was about to do the same for the index listing the collection of entities, when i thought I could simplify my controller by taking HAL stuff out of it!

The middleware signature looks like this:

namespace Psr\Http\Server;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

/**
 * Participant in processing a server request and response.
 *
 * An HTTP middleware component participates in processing an HTTP message:
 * by acting on the request, generating the response, or forwarding the
 * request to a subsequent middleware and possibly acting on its response.
 */
interface MiddlewareInterface
{
    /**
     * Process an incoming server request.
     *
     * Processes an incoming server request in order to produce a response.
     * If unable to produce the response itself, it may delegate to the provided
     * request handler to do so.
     */
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface;
}

So first up I’ll create my Middleware class and make it do nothing but pass stuff along as it was received

<?php

namespace Bone\Http\Middleware;

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

class HalEntity implements MiddlewareInterface
{
    /**
     * @param ServerRequestInterface $request
     * @param RequestHandlerInterface $handler
     * @return ResponseInterface
     */
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        return $handler->handle($request);
    }
}

Then register the middleware on your stack. For myself using league/router, you can add middleware to all routes, groups of routes, or individual routes, which is what I’ll do here:

$route->map('GET', '/dragon/{id:number}', [DragonApiController::class, 'viewAction'])
->middleware(new HalEntity());

A quick check, and yes, the page is still loading as per normal. So now to get the refactoring done!

First we strip out the HAL link stuff and the array merge, and move it to a class impolementing the PSR middleware interface, to make it look like this:

<?php

namespace Bone\Http\Middleware;

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

class HalEntity implements MiddlewareInterface
{
/**
* @param ServerRequestInterface $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$uri = $request->getUri();

$hal = [
'_links' => [
'self' => [
'href' => $uri->getScheme() . '://' . $uri->getHost() . $uri->getPath(),
]
],
];

$response = $handler->handle($request);

$data = \json_decode($response->getBody()->getContents(), true);
$data = \array_merge($hal, $data);

$body = $response->getBody();
$body->rewind();
$body->write(\json_encode($data));

return $response->withBody($body);
}
}

And your controller action will look like this 🙂 :

/**
 * @param ServerRequestInterface $request
 * @param array $args
 * @return ResponseInterface
 * @throws \Doctrine\ORM\EntityNotFoundException
 */
public function viewAction(ServerRequestInterface $request, array $args): ResponseInterface
{
    $dragon = $this->service->getRepository()->find($args['id']);

    return new JsonResponse($dragon->toArray() );
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s