3

In my router.php i have enabled the json extension with Routing File Extensions like this

Router::prefix('api', function (RouteBuilder $routes) {
    $routes->extensions(['json']);
    $routes->fallbacks('DashedRoute');
});

All is working good. When i want to give a error message i use this JSON structure

{
    "data": {
        "error": "Please provide username"
    }
}

When i throw a ForbiddenException i got structure like this

{
    "message": "Forbidden",
    "url": "/sfworxerp/api/v1/attendances/getEmployeesAttendance.json",
    "code": 403
}

But i need it in this format.

{
    "data": {
        "error": "Forbidden",
        "code": 403
    }
}

I have done so far

I have created a custom ExceptionRenderer like this

namespace App\Error;

use Cake\Error\ExceptionRenderer;

class AppExceptionRenderer extends ExceptionRenderer
{

    public function forbidden($error)
    {

        return 'test';
    }

}

and added it to my app.php file

'Error' => [
    'errorLevel' => E_ALL & ~E_DEPRECATED,
    'exceptionRenderer' => 'App\Error\AppExceptionRenderer',
    'skipLog' => [],
    'log' => true,
    'trace' => true,
],

When i change the value for string 'test' to an array it throw error

Fatal error: Call to a member function send() on a non-object in F:\public_html\sfworxerp\vendor\cakephp\cakephp\src\Error\ErrorHandler.php on line 186

Aman Rawat
  • 2,625
  • 1
  • 25
  • 40

2 Answers2

12

array is not a valid return type for custom error methods

Have a closer look at the docs, it states the two possible return values for custom exception renderer methods:

[...] Exception handling methods get the exception being handled as their argument. Your custom exception rendering can return either a string or a Response object. Returning a Response will give you full control over the response. [...]

* emphasis mine

Cookbook > Error & Exception Handling

So if you'd wanted a custom response for a specific method, you'd have to build the proper response via the $this->controller->response object and return that.

Modify what and how to serialize

However such custom methods do affect all error responses, not just the serialized ones. For a less invasive approach you better hook into for example _outputMessage() instead, and modify the view variables and serialization configuration to fit your needs, something along the lines of:

protected function _outputMessage($template)
{
    $this->controller->set('data', [
        'error' => $this->controller->viewVars['message'],
        'code' => $this->controller->viewVars['code']
    ]);
    $this->controller->set('_serialize', ['data']);

    return parent::_outputMessage($template);
}

That would give you the desired structure for every serialized error response (being it JSON, XML, whatever), while leaving regular error responses unaffected. That's just a very basic example of course, but you should get the point.

ndm
  • 59,784
  • 9
  • 71
  • 110
2

You need override render() method

use Cake\Error\ExceptionRenderer;

class AppExceptionRenderer extends ExceptionRenderer
{

    public function unauthorized()
    {
        // do some exception specific here
    }

    public function render()
    {
        // You can output here your desired response.
        // You can access exception by calling $this->exception
        // For example...

        $this->controller->set('response', ['foo' => 'bar']);
        $this->controller->set('_serialize', 'response');

       return $this->controller->response;
    }
}
makallio85
  • 1,366
  • 2
  • 14
  • 29