Sometimes an error occurs when pulling a controller (or one of its dependencies) from the container. In such cases, an ApiProblem is not returned; instead, the default error page for the application is returned, which can cause problems for API clients.
When a container error occurs, it is caught by the DispatchListener
, which then leads to triggering the dispatch.error
event. This event is not normally handled by Apigility, so we need to get creative, and create a listener that:
- detects if the triggered event is due to an exception
- detects if the controller leading to the exception is one managed by Apigility
- returns an
ApiProblemResponse
in such situations
The following is an example demonstrating the listener, a factory for creating it, and a sample Module
that wires the listener onBootstrap
.
namespace ApiProblemDispatchError;
use Psr\Container\ContainerInterface;
use Zend\Mvc\Application;
use Zend\Mvc\MvcEvent;
use ZF\ApiProblem\ApiProblem;
use ZF\ApiProblem\ApiProblemResponse;
class ApiProblemDispatchErrorListener
{
private $apiControllers;
public function __construct(array $controllers)
{
$this->apiControllers = $controllers;
}
public function __invoke(MvcEvent $e)
{
if ($e->getError() !== Application::ERROR_EXCEPTION) {
return;
}
if (! in_array($e->getController(), $this->apiControllers, true)) {
return;
}
$problem = new ApiProblem(500, $e->getParam());
return new ApiProblemResponse($problem);
}
}
class ApiProblemDispatchErrorListenerFactory
{
public function __invoke(ContainerInterface $container)
{
$config = $container->get('config');
$restControllers = isset($config['zf-rest']) ? array_keys($config['zf-rest']) : [];
$rpcControllers = isset($config['zf-rpc']) ? array_keys($config['zf-rpc']) : [];
$apiControllers = array_merge($restControllers, $rpcControllers);
return new ApiProblemDispatchErrorListener($apiControllers);
}
}
class Module
{
public function onBootstrap(MvcEvent $e)
{
$app = $e->getApplication();
$contaienr = $app->getServiceManager();
$events = $app->getEventManager();
$events->attach(MvcEvent::EVENT_DISPATCH_ERROR, $container->get(ApiProblemDispatchErrorListener::class), 100);
}
}
Reverse but related question: when you dispatch a controller which isn’t neither rest nor rpc you get an ApiProblemResponse from apigility because of UnauthorizedListener here https://github.com/zfcampus/zf-apigility/blob/master/src/MvcAuth/UnauthorizedListener.php#L27 . How to force apigility return ApiProblemResponse only for rest and rpc controllers?
This is for case ‘deny_by_default’ => true.
Please do not hijack other threads. This adds to much noise, watered down the original topic and irritates other readers.
Thank you in advance!