CakePHP 3: обработка исключений / сериализация в RESTful API - PullRequest
0 голосов
/ 08 мая 2018

Я создаю JSON RESTful API с CakePHP3, но я не уверен, что является лучшим подходом для обработки ошибок и предоставления клиенту информации об ошибке. Пока что мой подход - генерировать исключение HttpException, если (например) сбой при сохранении сущности из-за ошибок валидации.

В моем контроллере у меня есть следующее:

if (!$this->Categories->save($categoryEntity)) {
    throw new InternalErrorException('Saving failed');
}

$this->set('status', 'Everything fine!');
$this->set('_serialize', true);

Если сохранение не удается, исключение сериализуется в json, и ответ выглядит следующим образом:

{
 "message": "Saving failed",
 "url": "\/categories\/edit",
 "code": 500,
}

Теперь я хочу включить более подробную информацию об ошибке. Например что-то вроде этого:

{
 "message": "Saving failed",
 "errors":  "Validation error: Field id has to be numeric"
 "url": "\/categories\/edit",
 "code": 500,
}

Я пробовал это с расширенным HttpException, в котором ошибки принимались за дополнительный параметр, но этот дополнительный параметр не сериализуется. Как я могу включить некоторую дополнительную информацию в исключение или как я могу изменить поведение сериализации исключения в CakePHP3?

1 Ответ

0 голосов
/ 08 мая 2018

Сериализированные переменные представления для исключений жестко запрограммированы в средстве визуализации исключений, вам необходимо создать настраиваемую / расширенную переменную, которая будет обрабатывать ваше настраиваемое исключение, чтобы он мог получать дополнительные данные, которые он предоставляет.

Вот быстрый и грязный пример с использованием пользовательского исключения с именем ValidationErrorException (InternalErrorException уже используется ядром CakePHP), которое расширяет \Cake\Http\Exception\HttpException и реализует метод getValidationErrors(), который возвращает ошибки проверки:

// in src/Error/Exception/ValidationErrorException.php

namespace App\Error\Exception;

use Cake\Datasource\EntityInterface;
use Cake\Http\Exception\HttpException;

class ValidationErrorException extends HttpException
{
    protected $_validationErrors;

    public function __construct(EntityInterface $entity, $message = null, $code = 422)
    {
        $this->_validationErrors = $entity->getErrors();

        if ($message === null) {
            $message = 'A validation error occurred.';
        }

        parent::__construct($message, $code);
    }

    public function getValidationErrors()
    {
        return $this->_validationErrors;
    }
}

Такое исключение HTTP будет сопоставлено с методом класса средства визуализации исключений с соответствующим именем:

// in src/Error/AppExceptionRenderer.php

namespace App\Error;

use App\Error\Exception\ValidationErrorException;
use Cake\Error\ExceptionRenderer;

class AppExceptionRenderer extends ExceptionRenderer
{
    // HttpExceptions automatically map to methods matching the inflected variable name
    public function validationError(ValidationErrorException $exception)
    {
        $code = $this->_code($exception);
        $method = $this->_method($exception);
        $template = $this->_template($exception, $method, $code);

        $message = $this->_message($exception, $code);
        $url = $this->controller->request->getRequestTarget();

        $response = $this->controller->getResponse();
        foreach ((array)$exception->responseHeader() as $key => $value) {
            $response = $response->withHeader($key, $value);
        }
        $this->controller->setResponse($response->withStatus($code));

        $viewVars = [
            'message' => $message,
            'url' => h($url),
            'error' => $exception,
            'code' => $code,
            // set the errors as a view variable
            'errors' => $exception->getValidationErrors(),
            '_serialize' => [
                'message',
                'url',
                'code',
                'errors' // mark the variable as to be serialized
            ]
        ];
        $this->controller->set($viewVars);

        return $this->_outputMessage($template);
    }
}

В вашем контроллере вы можете затем выбросить его так, передав его сущности, которая не прошла проверку:

if (!$this->Categories->save($categoryEntity)) {
    throw new \App\Error\Exception\ValidationErrorException($categoryEntity);
}

Смотри также

...