I wanted to point people to a simple and framework-agnostic explanation of middleware. I found none. I wrote one.
What it looks like
The basic structure of an HTTP middleware implementation:
function middleware(Request $request, callable $next): Response {
// 1. Code that happens before the next middleware is invoked.
// Note: this code may alter the Request
// 2. Invoke the next middleware.
// Note: this step is optional too
$response = $next($request);
// 3. Code that happens after the next middleware returns.
// Note: this code may alter the Response
return $response ;
}
At its core, an HTTP middleware is a piece of code that returns a Response.
How it works
We can see from the code snippet, that each middleware is a wrapper around the next middleware.
An execution pipeline is composed by adding layers of such decoupled wrappers, usually around an app kernel.
Each middleware receives a Request and a reference to the next middleware in the pipeline (also called a handler).
A middleware can do 3 optional things:
- alter the Request (by injecting meta information for the rest of the app or pre-processing resources)
- delegate the execution to the next handler passing it the (altered) Request
- alter the Response returned by the next handler, or produce a completely new Response
💡
A middleware may choose not to invoke the next handler and terminate the pipeline. For example, this happens in an authorization middleware if a request is not authorized.
Finally, the pipeline is executed using a middleware dispatcher.
There is quite a number of dispatchers available and several frameworks ship with their own.
What it is for
Middleware provides a means to decompose a complex solution into smaller self-contained pieces of code.
The app kernel (controllers and such) can focus on business logic and avoid being bloated by authorization, authentication, CORS, caching, routing and other concerns that can be moved to HTTP middleware.
PSR-15
PSR-15 is a standard that defines two interfaces:
According to this standard, a middleware from the first snippet would look like this:
class Middleware implements MiddlewareInterface
{
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $next
): ResponseInterface
{
// 1. Code that happens before the next middleware is invoked.
// Note: this code may alter the Request
// 2. Invoke the next middleware.
// Note: this step is optional too
$response = $next->handle($request);
// 3. Code that happens after the next middleware returns.
// Note: this code may alter the Response
return $response ;
}
}
The Request and Response interfaces come from the PSR-7 standard.
To make it compatible with the first snippet which represents a functional middleware, we can simply implement the __invoke
magic method:
Code of the '__invoke' method
public function __invoke(
ServerRequestInterface $request,
callable $next
): ResponseInterface {
$handler = new class($next) implements RequestHandlerInterface {
private $next;
public function __construct(callable $next)
{
$this->next = $next;
}
public function handle(
ServerRequestInterface $request
): ResponseInterface
{
return ($this->next)($request);
}
};
return $this->process($request, $handler);
}
You can look up PSR-15 compatible middleware and dispatchers.
Conclusion
We have seen that middleware is just a self-contained piece of code that returns a Response.
We can compose layers of middleware to create an app.
Here are some resources to dig deeper:
👋
There are also other explanations of middleware, with different meanings in different contexts. This one refers to HTTP request handling pipeline.
Top comments (0)