Reference
Problem Details Middleware
While returning a problem details response from your own middleware is powerful and flexible, sometimes you may want a fail-safe way to return problem details for any exception or PHP error that occurs without needing to catch and handle them yourself.
For this purpose, this library provides ProblemDetailsMiddleware
.
This middleware does the following:
- Composes a
ProblemDetailsResponseFactory
. - Determines if the request accepts JSON or XML; if neither is accepted, it simply passes execution to the request handler.
- Registers a PHP error handler using the current
error_reporting
mask, and throwing any errors handled asErrorException
instances. - Wraps a call to the
$handler
in atry
/catch
block; if nothing is caught, it returns the response immediately. - For all caught throwables, it passes the throwable to
ProblemDetailsResponseFactory::createResponseFromThrowable()
to generate a Problem Details response.
As such, you can register this in middleware stacks in order to automate generation of problem details for exceptions and PHP errors.
As an example, using Expressive, you could compose it as an error handler within your application pipeline, having it handle all errors and exceptions from your application:
$app->pipe(ProblemDetailsMiddleware::class);
Or for a subpath of your application:
$app->pipe('/api', ProblemDetailsMiddleware::class);
Alternately, you could pipe it within a routed-middleware pipeline:
$app->get('/api/books', [
ProblemDetailsMiddleware::class,
BooksList::class,
], 'books');
This latter approach ensures that you are only providing problem details for specific API endpoints, which can be useful when you have a mix of APIs and traditional web content in your application.
Listeners
- Since 0.5.2
The ProblemDetailsMiddleware
allows you to register listeners to trigger
when it handles a Throwable
. Listeners are PHP callables, and the middleware
triggers them with the following arguments, in the following order:
Throwable $error
: the throwable/exception caught by the middleware.ServerRequestInterface $request
: the request as provided to theProblemDetailsMiddleware
.ResponseInterface $response
: the response theProblemDetailsMiddleware
generated based on the$error
.
Note that each of these arguments are immutable; you cannot change the state in a way that that state will propagate meaningfully. As such, you should use listeners for reporting purposes only (e.g., logging).
As an example:
// Where $logger is a PSR-3 logger implementation
$listener = function (
Throwable $error,
ServerRequestInterface $request,
ResponseInterface $response
) use ($logger) {
$logger->error('[{status}] {method} {uri}: {message}', [
'status' => $response->getStatusCode(),
'method' => $request->getMethod(),
'uri' => (string) $request->getUri(),
'message' => $error->getMessage(),
]);
};
Attach listeners to the ProblemDetailsMiddleware
instance using its
attachListener()
method:
$middleware->attachListener($listener);
Factory
The ProblemDetailsMiddleware
ships with a corresponding PSR-11 compatible factory,
ProblemDetailsMiddlewareFactory
. This factory uses the service named
Zend\ProblemDetails\ProblemDetailsResponseFactory
to instantiate the
middleware.
For Expressive 2+ users, this middleware should be registered automatically with your application on install, assuming you have the zend-component-installer plugin in place (it's shipped by default with the Expressive skeleton).
Registering listeners
- Since 0.5.2
In order to register listeners, we recommend using a
delegator factory
on the Zend\ProblemDetails\ProblemDetailsMiddleware
service.
As an example:
class LoggerProblemDetailsListenerDelegator
{
public function __construct(ContainerInterface $container, $serviceName, callable $callback)
{
$middleware = $callback();
$middleware->attachListener($container->get(LoggerProblemDetailsListener::class));
return $middleware;
}
}
You would then register this as a delegator factory in your configuration:
'delegators' => [
ProblemDetailsMiddleware::class => [
LoggerProblemDetailsListenerDelegator::class,
],
],
Found a mistake or want to contribute to the documentation? Edit this page on GitHub!