2012-07-20 19 views
8

Quiero hacer algo muy similar a this pero en el mundo de CakePHP para solicitudes AJAX. Por el momento estoy haciendo esto:Manejo de errores Ajax en CakePHP

$this->autoRender = false; 
$this->response->statusCode(500); 

Se basa fuera de this. Sin embargo, esta solución no me permite incluir un mensaje personalizado, como en el ejemplo de Rails, para que de esa manera, en el controlador de errores del lado de mis clientes, pueda mostrar el mensaje incluido en la respuesta de error 500.

¿Cómo implementaría la misma funcionalidad en CakePHP como el ejemplo de Ruby on Rails?

Respuesta

0

Puede utilizar CakeExceptions como se explica en el libro de cocina: http://book.cakephp.org/2.0/en/development/exceptions.htmlPERO si desea utilizar los mensajes personalizados no he encontrado ninguna otra manera que mediante debug = 1 en su modo de producción :(

Aquí está mi enfoque con construido en métodos:

En su controlador:

if($this->request->is('ajax')){ 
    Configure::write('debug', 1); 
} 

if(!$allowed) { 
    throw new InternalErrorException('Keep your fingers away from me!'); // 500 error 
} 

Cambio la plantilla de error a la salida nada más que el error cuando se utiliza en llamadas AJAX en /app/View/Errors/error500.ctp:

<?php 
if($this->request->is('ajax')): 
    // Output for AJAX calls 
    echo $name; 

else: 
    //Standard CakePHP output ?> 
    <h2><?php echo $name; ?></h2> 
    <p class="error"> 
     <strong><?php echo __d('cake', 'Error'); ?>: </strong> 
     <?php echo __d('cake', 'An Internal Error Has Occurred.'); ?> 
    </p> 
    <?php 
    if (Configure::read('debug') > 0): 
     echo $this->element('exception_stack_trace'); 
    endif; 

endif; ?> 

A continuación, puede analizar el texto devuelto en su AJAX. Aquí están las piezas de jQuery que utilizo:

//... 
error: function (request) { 
    yourErrorShowingFunction(_this, request.responseText); 
} 
//... 

Espero que esto ayude :)

Si alguien tiene una idea de cómo utilizar los errores personalizados en el modo de producción (sin sobrescribir el modo de depuración) Estaría ¡muy feliz!

+0

Aunque esta no era la respuesta que esperaba, ya que implica que estoy en modo de depuración, lo marcaré como correcto ya que nadie más parece tener alguna idea al respecto. ¡Gracias por el esfuerzo! –

2

También he tenido problemas con las excepciones personalizadas y los códigos de error cuando uso las solicitudes ajax (jquery mobile en mi caso). Aquí está la solución que se me ocurrió, sin involucrar sobrescribir el modo de depuración. Lanza errores personalizados en el modo de desarrollo, y también opcionalmente en modo de producción. Espero que ayude a alguien:

AppExceptionRenderer.php:

<?php 
App::uses('ExceptionRenderer', 'Error'); 

class AppExceptionRenderer extends ExceptionRenderer 
{ 
    public function test($error) 
    { 
     $this->_sendAjaxError($error); 
    } 

    private function _sendAjaxError($error) 
    { 
     //only allow ajax requests and only send response if debug is on 
     if ($this->controller->request->is('ajax') && Configure::read('debug') > 0) 
     { 
      $this->controller->response->statusCode(500); 
      $response['errorCode'] = $error->getCode(); 
      $response['errorMessage'] = $error->getMessage(); 
      $this->controller->set(compact('response')); 
      $this->controller->layout = false; 
      $this->_outputMessage('errorjson'); 
     } 
    } 
} 

Usted puede dejar de lado Configure::read('debug') > 0 si desea mostrar la excepción en modo de depuración. La vista errorjson.ctp se encuentra en 'Error/errorjson.CTP ':

<?php 
echo json_encode($response); 
?> 

En este caso mi excepción se llama

TestException

y se define de la siguiente manera:

<?php 
class TestException extends CakeException { 
    protected $_messageTemplate = 'Seems that %s is missing.'; 

    public function __construct($message = null, $code = 2) { 
     if (empty($message)) { 
        $message = 'My custom exception.'; 
      } 
      parent::__construct($message, $code); 
    } 
} 

donde tengo un código de error personalizado 2, $code = 2, para mi respuesta json. La respuesta ajax arrojará un error 500 con los siguientes datos JSON:

{"errorCode":"2","errorMessage":"My custom exception."} 

Obviamente, también es necesario para iniciar la excepción de su controlador:

throw new TestException(); 

e incluye el procesador de excepción http://book.cakephp.org/2.0/en/development/exceptions.html#using-a-custom-renderer-with-exception-renderer-to-handle-application-exceptions

Esto puede estar un poco fuera de alcance, pero para manejar la respuesta de error ajax en JQuery utilizo:

$(document).ajaxError(function (event, jqXHR, ajaxSettings, thrownError) { 
    //deal with my json error 
}); 
+0

Funciona muy bien. Gracias. – MotsManish

7

Como se mencionó anteriormente, las excepciones son la forma de devolver un error en una solicitud de AJAX en CakePHP. Aquí está mi solución para obtener un control más preciso de cómo se ve el error. Además, como se indicó anteriormente, estoy usando un Renderizador de excepciones personalizado, pero no una excepción personalizada. La respuesta de error por defecto es un objeto JSON como esto:

{"name":"An Internal Error Has Occurred", "url": "\/users\/login.json"} 

I casi como la forma en que el procesador predeterminado controla los errores de AJAX; Sólo quiero ajustar un poco:

<?php 
// File: /app/Lib/Error/CustomExceptionRenderer.php 
App::uses('ExceptionRenderer', 'Error'); 
class CustomExceptionRenderer extends ExceptionRenderer { 

    // override 
    public function error400($error) { 
     $this->_prepareView($error, 'Not Found'); 
     $this->controller->response->statusCode($error->getCode()); 

     $this->_outputMessage('error400'); 
    } 

    // override 
    public function error500($error) { 
     $this->_prepareView($error, 'An Internal Error Has Ocurred.'); 
     $code = ($error->getCode() > 500 && $error->getCode() < 506) ? $error->getCode() : 500; 
     $this->controller->response->statusCode($code); 

     $this->_outputMessage('error500'); 
    } 

    private function _prepareView($error, $genericMessage) { 
     $message = $error->getMessage(); 
     if(!Configure::read('debug') && !Configure::read('detailed_exceptions')) { 
      $message = __d('cake', $genericMessage); 
     } 
     $url = $this->controller->request->here(); 
     $renderVars = array(
      'name' => h($message), 
      'url' => h($url), 
      ); 
     if(isset($this->controller->viewVars['csrf_token'])) { 
      $renderVars['csrf_token'] = $this->controller->viewVars['csrf_token']; 
     } 
     $renderVars['_serialize'] = array_keys($renderVars); 
     $this->controller->set($renderVars); 
    } 
} 

Luego, en Bootstrap.php:

Configure::write('Exception.renderer', 'CustomExceptionRenderer'); 

Así que aquí es cómo funciona:

  • decir que quiero devolver una nueva CSRF token en mi respuesta de error, de modo que si mi token existente ha caducado antes de que se lanzara la excepción, no me piratearán la próxima vez que pruebe la solicitud. Consulte Security Component documentation para obtener más información sobre la protección CSRF.
  • Crea una nueva clase en la aplicación/Lib/Error. Puede extender el renderizador predeterminado, o no. Como solo quiero cambiar algunas cosas pequeñas, y para mantener el ejemplo simple, lo estoy extendiendo.
  • Reemplace los métodos que utiliza el representador predeterminado para crear el objeto JSON que se devolverá. Esto se hace a través del Request Handler Component, y se ajusta a las mejores prácticas. De hecho, el renderizador predeterminado hace lo mismo.
  • Nuevo método privado para mantener las cosas SECAS.
  • Mi solución al problema de no recibir mensajes de error personalizados en producción es agregar una clave de configuración opcional. De forma predeterminada, esta clase mostrará los mensajes genéricos en producción, pero si tiene depuración configurada en 0 y quiere los mensajes de error específicos: Configure::write('detailed_exceptions', 1);
  • Agregue el nuevo token a la respuesta si existe.En mi caso, ya he llamado al Controller::set en el nuevo token en el método beforeFilter de AppController, por lo que está disponible en $this->controller->viewVars. Probablemente haya docenas de otras formas de lograr esto.

Ahora su respuesta se parece a esto:

{ 
    "name":"The request has been black-holed", 
    "url":"\/users\/login.json", 
    "csrf_token":"1279f22f9148b6ff30467abaa06d83491c38e940" 
} 

cualquier dato adicional que, de cualquier tipo puede ser añadido a la matriz pasada a Controller::set por el mismo resultado.

+0

esto realmente me ayuda mucho. –