MixerAPI ExceptionRender
This plugin handles rendering entity validation errors and other exceptions for your API.
- Integrates with Validator on
add()
andedit()
actions. - Adds the short name of the Exception thrown to the response
Read more at MixerAPI.com.
Installation
You can skip this step if you have MixerApi installed.
composer require mixerapi/exception-render
bin/cake plugin load MixerApi/ExceptionRender
Alternatively after composer installing you can manually load the plugin in your Application:
# src/Application.php
public function bootstrap(): void
{
// other logic...
$this->addPlugin('MixerApi/ExceptionRender');
}
Setup
In your config/app.php
file change the default exceptionRenderer
:
'Error' => [
'errorLevel' => E_ALL,
'exceptionRenderer' => MixerApi\ExceptionRender\MixerApiExceptionRenderer::class,
'skipLog' => [],
'log' => true,
'trace' => true,
],
Usage
Define your Validations as normal in your Table classes and MixerApiExceptionRenderer
handles the rest by attaching
a listener to the afterMarshall event which fires
when request data is merged into entities during patchEntity() or newEntity() calls. If a validation fails then a
ValidationException
is thrown and rendered with an HTTP 422 status code.
Example controller action:
public function add()
{
$this->request->allowMethod('post');
$actor = $this->Actors->newEmptyEntity();
$actor = $this->Actors->patchEntity($actor, $this->request->getData()); // potential ValidationException here
if ($this->Actors->save($actor)) {
$this->viewBuilder()->setOption('serialize', 'actor');
$this->set('actor', $actor);
return;
}
throw new \Exception("Record failed to save");
}
Output:
{
"exception": "ValidationException",
"message": "Error saving resource `Actor`",
"url": "/actors",
"code": 422,
"violations": [
{
"propertyPath": "first_name",
"messages": [
{
"rule": "_required",
"message": "This field is required"
}
]
},
{
"propertyPath": "last_name",
"messages": [
{
"rule": "_required",
"message": "This field is required"
}
]
}
]
}
Using the controller example from above, we can catch the exception if desired and perform additional logic:
try {
$actor = $this->Actors->newEmptyEntity();
$actor = $this->Actors->patchEntity($actor, $this->request->getData());
} catch (\MixerApi\ExceptionRender\ValidationException $e) {
// do something here
}
Exceptions
For non-validation based exceptions, even your projects own custom exceptions, the output is similar to CakePHP native
output with the addition of an exception attribute. For example, a MethodNotAllowedException
would result in:
{
"exception": "MethodNotAllowedException",
"message": "Your exception message here",
"url": "/actors",
"code": 405
}
If for instance you have a custom exception that is thrown, such as InventoryExceededException
, you would see:
{
"exception": "InventoryExceededException",
"message": "No inventory exists",
"url": "/requested-url",
"code": 500
}
Providing an Exception name, in conjunction with the status code already provided by CakePHP, enables API clients to tailor their exception handling.
Changing Error Messages
ExceptionRender dispatches a MixerApi.ExceptionRender.beforeRender
event that you can listen for to alter viewVars
and serialize
variables. Both are accessible via the MixerApi\ExceptionRender\ErrorDecorator
.
Example:
<?php
declare(strict_types=1);
namespace App\Event;
use Cake\Event\EventListenerInterface;
use MixerApi\ExceptionRender\ErrorDecorator;
use MixerApi\ExceptionRender\MixerApiExceptionRenderer;
class ExceptionRender implements EventListenerInterface
{
public function implementedEvents(): array
{
return [
'MixerApi.ExceptionRender.beforeRender' => 'beforeRender'
];
}
/**
* @param \Cake\Event\Event $event
*/
public function beforeRender(\Cake\Event\Event $event)
{
$errorDecorator = $event->getSubject();
$data = $event->getData();
if (!$errorDecorator instanceof ErrorDecorator || !$data['exception'] instanceof MixerApiExceptionRenderer) {
return;
}
if (!$data['exception']->getError() instanceof \Authentication\Authenticator\UnauthenticatedException) {
return;
}
$viewVars = $errorDecorator->getViewVars();
$viewVars['message'] = 'A custom unauthenticated message';
$errorDecorator->setViewVars($viewVars);
}
}
Read more about Events in the official CakePHP documentation.