2017-05-08 24 views
2

Symfony와이 번들 FosRestBundle, jms/serializer-bundle, lexik/jwt-authentication-bundle을 사용하여 api rest 서버를 만듭니다. Symfony 3 API REST - JSON 응답 형식에 예외 예외 처리 시도

어떻게 이렇게 깨끗한 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 [email protected] exists in database."} 

대신에 시스템 메시지의 :

UniqueConstraintViolationException in AbstractMySQLDriver.php line 66: An exception occurred while executing 'INSERT INTO user (email, name, role, password, is_active) VALUES (?, ?, ?, ?, ?)' with params ["[email protected]", "etienne", "ROLE_USER", "$2y$13$tYW8AKQeDYYWvhmsQyfeme5VJqPsll\/7kck6EfI5v.wYmkaq1xynS", 1]: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '[email protected]' for key 'UNIQ_8D93D649E7927C74' 

반환 이름 필수 또는 놓친 필드 깨끗한 JSON의 reponse를. 여기

내 컨트롤러 :

<?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())); 
} 
} 

감사

+0

이 코드가 제대로 실행 되었습니까? 난 그냥 기존의 심포니 프로젝트에서 그것을 시도하고'catch()'예상대로 작동합니다 – kero

+0

코드가 작동하지만 json 형식에 대한 사용자 지정 메시지 오류 싶습니다. – kaneda

+0

아, 죄송합니다. 질문을 잘못 읽었습니다. 따라서 누락 된 필드 또는 중복 된 항목의 이름을 반환 하시겠습니까? rafrsr의 대답으로이 문제가 해결 되었습니까? – kero

답변

5

우리는 다음과 같은 방법 사용 : ApiException이

을 확장 유효성 검사 예외를 작성합니다

class ApiException extends \Exception 
{ 
    public function getErrorDetails() 
    { 
     return [ 
      'code' => $this->getCode() ?: 999, 
      'message' => $this->getMessage()?:'API Exception', 
     ]; 
    } 
} 

: API 예외

일반 클래스를 양식 오류

if ($form->getErrors(true)->count()) { 
    throw new ValidationException($form); 
} 

당신의 ExceptionController를 만들고 구성

이있는 경우
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; 
    } 
} 

는 컨트롤러에서 예외를 사용

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'); 
    } 
} 

config.yml

fos_rest: 
    #... 
    exception: 
     enabled: true 
     exception_controller: 'SomeBundle\Controller\ExceptionController::showAction' 

참조 : http://symfony.com/doc/current/bundles/FOSRestBundle/4-exception-controller-support.html

이 접근 방식을 사용하면 다른 유형의 오류 (API 설명서에 유용한)에 대한 사용자 지정 메시지 및 코드를 사용하여 사용자 지정 예외를 만들 수 있습니다. 예외가 발생할 경우 API 소비자에게만 "내부 서버 오류"를 보여주는 다른 내부 예외를 숨 깁니다. APIException에서 확장되지 않습니다.