With the double-pass function signature ($request, $response, $next) being removed from middleware in Slim 4 I'm trying to figure out how to short circuit the middleware chain. See #2335 for an other explanation of how this is done in Slim 3.
For my example I am using the double pass function to check for a valid API key in middleware. If the API key isn't valid then I return a response indicating an error status. If the API key is valid then I would simply call $next($request, $response) which continues the middleware chain.
The question is how do I short circuit the middleware in Slim 4?
You just return the response early without calling $handler->handle($request). Essentially $handler->handle($request) is the equivalent of $next($request, $response);
<?php
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Slim\Psr7\Response;
class MyMiddleware implements MiddlewareInterface {
public function process(Request $request, RequestHandler $handler): ResponseInterface
{
if ($conditionToShortCircuit) {
$response = new Response();
$response->getBody()->write('Short circuit');
return $response;
}
// Keep processing middleware queue as normal
return $handler->handle($request);
}
}
Note that to stay decoupled from the PSR7-implementation used, you could write your middleware as follows
<?php
declare(strict_types=1);
namespace App;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class MyMiddleware implements MiddlewareInterface
{
/**
* @var ResponseFactoryInterface
*/
private $responseFactory;
/**
* @param ResponseFactoryInterface $responseFactory
*/
public function __construct(ResponseFactoryInterface $responseFactory)
{
$this->responseFactory = $responseFactory;
}
/**
* @inheritDoc
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
if ($conditionToShortCircuit) {
$response = $this->responseFactory->createResponse();
$response->getBody()->write('Short circuit');
return $response;
}
// Keep processing middleware queue as normal
return $handler->handle($request);
}
}
and upon instantiation you pass the response factory to the middleware
<?php
declare(strict_types=1);
use App\MyMiddleware;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
require __DIR__ . '/../vendor/autoload.php';
$app = AppFactory::create();
$app->addMiddleware(new MyMiddleware($app->getResponseFactory()));
$app->addRoutingMiddleware();
$app->addErrorMiddleware(true, true, false);
$app->get('/', function (Request $request, Response $response, array $args): Response {
return $response;
});
$app->run();
Most helpful comment
You just return the response early without calling
$handler->handle($request). Essentially$handler->handle($request)is the equivalent of$next($request, $response);