csu*_*csu 2 json exception symfony fosrestbundle symfony-3.2
我使用Symfony创建了api rest服务器,这些包括FosRestBundle,jms/serializer-bundle,lexik/jwt-authentication-bundle.
我该如何发送一个干净的json响应格式,如下所示:
Missing field "NotNullConstraintViolationException"
{'status':'error','message':"Column 'name' cannot be null"}
or
{'status':'error','message':"Column 'email' cannot be null"}
Or Duplicate entry "UniqueConstraintViolationException" :
{'status':'error','message':"The email user1@gmail.com exists in database."}
Run Code Online (Sandbox Code Playgroud)
而不是系统消息:
UniqueConstraintViolationException in AbstractMySQLDriver.php line 66: An exception occurred while executing 'INSERT INTO user (email, name, role, password, is_active) VALUES (?, ?, ?, ?, ?)' with params ["user1@gmail.com", "etienne", "ROLE_USER", "$2y$13$tYW8AKQeDYYWvhmsQyfeme5VJqPsll\/7kck6EfI5v.wYmkaq1xynS", 1]: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'user1@gmail.com' for key 'UNIQ_8D93D649E7927C74'
Run Code Online (Sandbox Code Playgroud)
返回一个干净的json响应,其名称为必填字段或错过字段.
我的控制器:
<?php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use AppBundle\Entity\User;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Symfony\Component\Debug\ExceptionHandler;
use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\HttpFoundation\JsonResponse;
use FOS\RestBundle\Controller\Annotations as Rest; // alias pour toutes les annotations
class DefaultController extends Controller
{
/**
* @Rest\View()
* @Rest\Post("/register")
*/
public function registerAction(Request $request)
{
//catch all errors and convert them to exceptions
ErrorHandler::register();
$em = $this->get('doctrine')->getManager();
$encoder = $this->container->get('security.password_encoder');
$username = $request->request->get('email');
$password = $request->request->get('password');
$name = $request->request->get('name');
$user = new User($username);
$user->setPassword($encoder->encodePassword($user, $password));
$user->setName($name);
$user->setRole('ROLE_USER');
try {
$em->persist($user);
$em->flush($user);
}
catch (NotNullConstraintViolationException $e) {
// Found the name of missed field
return new JsonResponse();
}
catch (UniqueConstraintViolationException $e) {
// Found the name of duplicate field
return new JsonResponse();
}
catch ( \Exception $e ) {
//for debugging you can do like this
$handler = new ExceptionHandler();
$handler->handle( $e );
return new JsonResponse(
array(
'status' => 'errorException',
'message' => $e->getMessage()
)
);
}
return new Response(sprintf('User %s successfully created', $user->getUsername()));
}
}
Run Code Online (Sandbox Code Playgroud)
谢谢
我们使用以下方法:
API异常的通用类:
class ApiException extends \Exception
{
public function getErrorDetails()
{
return [
'code' => $this->getCode() ?: 999,
'message' => $this->getMessage()?:'API Exception',
];
}
}
Run Code Online (Sandbox Code Playgroud)
创建扩展ApiException的验证异常
class ValidationException extends ApiException
{
private $form;
public function __construct(FormInterface $form)
{
$this->form = $form;
}
public function getErrorDetails()
{
return [
'code' => 1,
'message' => 'Validation Error',
'validation_errors' => $this->getFormErrors($this->form),
];
}
private function getFormErrors(FormInterface $form)
{
$errors = [];
foreach ($form->getErrors() as $error) {
$errors[] = $error->getMessage();
}
foreach ($form->all() as $childForm) {
if ($childForm instanceof FormInterface) {
if ($childErrors = $this->getFormErrors($childForm)) {
$errors[$childForm->getName()] = $childErrors;
}
}
}
return $errors;
}
}
Run Code Online (Sandbox Code Playgroud)
当表单有错误时,在控制器中使用异常
if ($form->getErrors(true)->count()) {
throw new ValidationException($form);
}
Run Code Online (Sandbox Code Playgroud)
创建和配置ExceptionController
class ExceptionController extends FOSRestController
{
public function showAction($exception)
{
$originException = $exception;
if (!$exception instanceof ApiException && !$exception instanceof HttpException) {
$exception = new HttpException($this->getStatusCode($exception), $this->getStatusText($exception));
}
if ($exception instanceof HttpException) {
$exception = new ApiException($this->getStatusText($exception), $this->getStatusCode($exception));
}
$error = $exception->getErrorDetails();
if ($this->isDebugMode()) {
$error['exception'] = FlattenException::create($originException);
}
$code = $this->getStatusCode($originException);
return $this->view(['error' => $error], $code, ['X-Status-Code' => $code]);
}
protected function getStatusCode(\Exception $exception)
{
// If matched
if ($statusCode = $this->get('fos_rest.exception.codes_map')->resolveException($exception)) {
return $statusCode;
}
// Otherwise, default
if ($exception instanceof HttpExceptionInterface) {
return $exception->getStatusCode();
}
return 500;
}
protected function getStatusText(\Exception $exception, $default = 'Internal Server Error')
{
$code = $this->getStatusCode($exception);
return array_key_exists($code, Response::$statusTexts) ? Response::$statusTexts[$code] : $default;
}
public function isDebugMode()
{
return $this->getParameter('kernel.debug');
}
}
Run Code Online (Sandbox Code Playgroud)
config.yml
fos_rest:
#...
exception:
enabled: true
exception_controller: 'SomeBundle\Controller\ExceptionController::showAction'
Run Code Online (Sandbox Code Playgroud)
请参阅:http://symfony.com/doc/current/bundles/FOSRestBundle/4-exception-controller-support.html
使用此方法可以创建自定义异常,其中包含针对每种类型的错误的自定义消息和代码(对API文档有用),另一方面隐藏其他内部异常,仅向API使用者显示"内部服务器错误",当抛出的异常未从APIException.