2017-05-08 12 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

このコードは正しく実行されていますか?私はちょうど既存のsymfonyプロジェクトでそれを試して、 '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例外の

ジェネリッククラスを http://symfony.com/doc/current/bundles/FOSRestBundle/4-exception-controller-support.html

:フォームは

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

があなたの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'); 
    } 
} 

config.yml

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

参照を作成し、設定エラーがあるとき

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

は、あなたのコントローラで例外を使用します

このアプローチを使用すると、カスタムメッセージと各タイプのエラーコード(APIドキュメントに役立つ)でカスタム例外を作成することができます。例外がスローされたときに、APIコンシューマにのみ「内部サーバーエラー」 APIExceptionから拡張されていません。

関連する問題