Quick Start

Mapping Routes to Middleware and Request Handlers

The first step is to map a route to PSR-15 middleware or request handler. This looks like any other routing configuration, with small changes: the controller key in the routing options has to be the Laminas\Mvc\Middleware\PipeSpec class literal, and you provide a middleware key.

For example, to register an AlbumListHandler located in the module/Application/Handler directory to the routes of your Application module, add the following route to module/Application/config/module.config.php:

use Application\Handler\AlbumListHandler;
use Laminas\Mvc\Middleware\PipeSpec;
use Laminas\Router\Http\Literal;

return [
    'router' => [
        'routes' => [
            'album-list' => [
                'type' => Literal::class,
                'options' => [
                    'route' => '/albums',
                    'defaults' => [
                        'controller' => PipeSpec::class,
                        'middleware' => AlbumListHandler::class,
                    ],
                ],
            ],
        ],
    ],
];

Middleware may be provided as instance of a PSR-15 Psr\Http\Server\MiddlewareInterface or Psr\Http\Server\RequestHandlerInterface or as service name strings resolving to such instances.
You may also specify an instance of the PipeSpec class which accepts both middleware types above or their service name strings, or a Closure . These will then be piped into a Laminas\Stratigility\MiddlewarePipe instance in the order in which they are present in the PipeSpec. See routing middleware for examples.

No Action Required

Unlike action controllers, middleware typically is single purpose, and, as such, does not require a default action parameter.

Middleware Services

Middleware are pulled from the application service manager, unlike controllers in a normal laminas-mvc dispatch cycle, which are pulled from a dedicated ControllerManager.

Middleware retrieved must be a PSR-15 MiddlewareInterface or RequestHandlerInterface instance. Otherwise, MiddlewareListener will create an error response.

Writing Middleware

The next step is writing actual middleware to dispatch. PSR-15 defines two different interfaces:

MiddlewareInterface vs. RequestHandlerInterface

Middleware is code sitting between a request and a response; it typically analyzes the request to aggregate incoming data, delegates it to another layer to process, and then creates and returns a response.

A RequestHandler is a class that receives a request and returns a response, without delegating to other layers of the application. This is generally the inner-most layer of your application.

For more in-depth documentation visit the documentation for Mezzio and Stratigility or the PSR-15 specification.

Request Handlers

namespace Application\Handler;

use Application\Entity\Album;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Server\RequestHandlerInterface;

class AlbumDetailMiddleware implements RequestHandlerInterface
{
    /** @var ResponseFactoryInterface */
    private $responseFactory;
    /** @var StreamFactoryInterface */
    private $streamFactory;

    public function __construct(ResponseFactoryInterface $responseFactory, StreamFactoryInterface $streamFactory)
    {
        $this->responseFactory = $responseFactory;
        $this->streamFactory   = $streamFactory;
    }

    public function handle(ServerRequestInterface $request): ResponseInterface
    {
        /** @var Album $album */
        $album = $request->getAttribute('album');

        $body = $this->streamFactory->createStream('The name of the album is: ' . $album->getName());
        return $this->responseFactory->createResponse()->withBody($body);
    }
}

RequestHandlers resemble a single MVC Controller action, and will be used as the primary application functionality when dispatching a request.

Middleware

namespace Application\Middleware;

use Application\Repository\AlbumRepositoryInterface;
use Fig\Http\Message\StatusCodeInterface;
use Laminas\Router\RouteMatch;
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 AlbumFromRouteMiddleware implements MiddlewareInterface
{
    /** @var AlbumRepositoryInterface */
    private $albumRepository;
    /** @var ResponseFactoryInterface */
    private $responseFactory;

    public function __construct(AlbumRepositoryInterface $albumRepository, ResponseFactoryInterface $responseFactory)
    {
        $this->albumRepository = $albumRepository;
        $this->responseFactory = $responseFactory;
    }

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        /** @var RouteMatch $routeMatch */
        $routeMatch = $request->getAttribute(RouteMatch::class);
        $albumId    = $routeMatch->getParam('album_id');
        $album      = $this->albumRepository->findById($albumId);

        // if no album was found, we short-circuit the pipe and return a 404 error:
        if ($album === null) {
            return $this->responseFactory->createResponse(
                StatusCodeInterface::STATUS_NOT_FOUND,
                sprintf('Album with ID %s not found!', $albumId)
            );
        }

        // ...otherwise we populate the request with the album and call the RequestHandler
        $request = $request->withAttribute('album', $album);
        return $handler->handle($request);
    }
}

Middleware can return a direct response, in effect short-circuiting the middleware pipe, or pass request further while having a chance to act on passed request or returned response. Middleware in laminas-mvc is similar to routed middleware in Mezzio.

laminas-mvc does not have a global middleware pipe, so middleware can not be piped in front of MVC controllers.

Middleware Return Values

As middleware returns a PSR-7 response (Psr\Http\Message\ResponseInterface) it is converted back to a laminas-http response and returned by the MiddlewareListener, causing the application to short-circuit and return the response immediately.