CakePHP 3:RESTful API 中的异常处理/序列化



我正在使用CakePHP3构建一个JSON RESTful API,但我不确定处理错误并向客户端提供有关错误信息的最佳方法是什么。到目前为止,我的方法是在(例如)由于验证错误而保存实体失败时抛出 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 进行过,该扩展 HttpException 将错误作为额外参数,但该额外参数未序列化。如何在异常中包含一些额外的信息,或者如何在 CakePHP3 中更改异常的序列化行为?

异常的序列化视图变量在异常呈现器中硬编码,您必须创建一个处理自定义异常的自定义/扩展变量,以便它可以获取它提供的其他数据。

这是一个快速而肮脏的示例,使用名为ValidationErrorException的自定义异常(InternalErrorException已被 CakePHP 核心使用),它扩展了CakeHttpExceptionHttpException,并实现了返回验证错误的getValidationErrors()方法:

// in src/Error/Exception/ValidationErrorException.php
namespace AppErrorException;
use CakeDatasourceEntityInterface;
use CakeHttpExceptionHttpException;
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 AppError;
use AppErrorExceptionValidationErrorException;
use CakeErrorExceptionRenderer;
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 AppErrorExceptionValidationErrorException($categoryEntity);
}

参见

  • 说明书> 错误和异常处理> 更改 ExceptionRenderer
  • Source> \Cake\Error\ExceptionRenderer::render()