The middleware library provides developers a way of defining route middleware for their applications. Middleware are simply layers of request processing before and after a controller action is invoked. This is extremely useful for actions like authorization, logging, and request/response decoration.
Middleware have a simple signature:
use Aphiria\Net\Http\Handlers\IRequestHandler;
use Aphiria\Net\Http\{IHttpRequestMessage, IHttpResponseMessage};
interface IMiddleware
{
public function handle(IHttpRequestMessage $request, IRequestHandler $next): IHttpResponseMessage;
}
IMiddleware::handle()
takes in the current request and
- Optionally manipulates it
- Passes the request on to the next request handler in the pipeline
- Optionally manipulates the response returned by the next request handler
- Returns the response
To manipulate the request before it gets to the controller, make changes to it before calling $next($request)
:
use Aphiria\Middleware\IMiddleware;
use Aphiria\Net\Http\Handlers\IRequestHandler;
use Aphiria\Net\Http\{IHttpRequestMessage, IHttpResponseMessage};
final class RequestManipulator implements IMiddleware
{
public function handle(IHttpRequestMessage $request, IRequestHandler $next): IHttpResponseMessage
{
// Do our work before returning $next->handle($request)
$request->getProperties()->add('Foo', 'bar');
return $next->handle($request);
}
}
To manipulate the response after the controller has done its work, do the following:
use Aphiria\Middleware\IMiddleware;
use Aphiria\Net\Http\Handlers\IRequestHandler;
use Aphiria\Net\Http\{IHttpRequestMessage, IHttpResponseMessage};
final class ResponseManipulator implements IMiddleware
{
public function handle(IHttpRequestMessage $request, IRequestHandler $next): IHttpResponseMessage
{
$response = $next->handle($request);
// Make our changes
$response->getHeaders()->add('Foo', 'bar');
return $response;
}
}
Occasionally, you'll find yourself wanting to pass primitive values to middleware to indicate something such as a required role to execute an action. In these cases, your middleware should extend Aphiria\Middleware\AttributeMiddleware
:
use Aphiria\Middleware\AttributeMiddleware;
use Aphiria\Net\Http\Handlers\IRequestHandler;
use Aphiria\Net\Http\{HttpException, IHttpRequestMessage, IHttpResponseMessage};
final class RoleMiddleware extends AttributeMiddleware
{
private IAuthService $authService;
// Inject any dependencies your middleware needs
public function __construct(IAuthService $authService)
{
$this->authService = $authService;
}
public function handle(IHttpRequestMessage $request, IRequestHandler $next): IHttpResponseMessage
{
$accessToken = null;
if (!$request->getHeaders()->tryGetFirst('Authorization', $accessToken)) {
return new Response(401);
}
if ($this->authService->accessTokenIsValid($accessToken)) {
return new Response(403);
}
// Attributes are available via $this->attributes
if (!$this->authService->accessTokenHasRole($accessToken, $this->attributes['role'])) {
return new Response(403);
}
return $next->handle($request);
}
}
To actually specify role
, pass it into your route configuration:
$routes->get('foo')
->toMethod(MyController::class, 'myMethod')
->withMiddleware(RoleMiddleware::class, ['role' => 'admin']);
Typically, middleware are wrapped in request handlers (eg MiddlewareRequestHandler
) and executed in a pipeline. You can create this pipeline using MiddlewarePipelineFactory
:
use Aphiria\Middleware\MiddlewarePipelineFactory;
use Aphiria\Net\Http\RequestFactory;
// Assume these are defined by your application
$loggingMiddleware = new LoggingMiddleware();
$authMiddleware = new AuthenticationMiddleware();
$controllerHandler = new ControllerRequestHandler();
$pipeline = (new MiddlewarePipelineFactory)->createPipeline(
[$loggingMiddleware, $authMiddleware],
$controllerHandler
);
$pipeline
will itself be a request handler, which you can then send a request through and receive a response:
$request = (new RequestFactory)->createRequestFromSuperglobals($_SERVER);
$response = $pipeline->handle($request);