2012-07-20 20 views
8

Voglio fare qualcosa di molto simile a this ma nel mondo CakePHP per richieste AJAX. Al momento sto facendo questo:Gestione errori Ajax in CakePHP

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

Si basa fuori di this. Tuttavia questa soluzione non mi consente di includere un messaggio personalizzato come nell'esempio Rails, in questo modo, nel gestore degli errori del mio client, posso visualizzare il messaggio incluso nella risposta dell'errore 500.

Come implementare la stessa funzionalità in CakePHP come nell'esempio Ruby on Rails?

risposta

0

È possibile utilizzare CakeExceptions come spiegato nel libro di cucina: http://book.cakephp.org/2.0/en/development/exceptions.htmlMA se si desidera utilizzare i messaggi personalizzati ho trovato nessun altro modo che usare debug = 1 nel vostro modo di produzione :(

Ecco il mio approccio con costruito in metodi:

nel controller:

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

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

Change il modello di errore per l'uscita nulla, ma l'errore quando usato in AJAX chiama /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; ?> 

È quindi possibile analizzare il testo restituito nel vostro AJAX. Ecco le parti jQuery che uso:

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

Spero che questo aiuti :)

Se qualcuno ha un'idea di come utilizzare gli errori personalizzati in modalità di produzione (senza sovrascrivere la modalità di debug) sarei molto felice!

+0

Anche se questo non era la risposta che speravo perché involes essere in modalità debug, ti segnalo come corretta dal momento che nessun altro sembra avere qualche idea su questo. Grazie per lo sforzo! –

2

Ho anche faticato con le eccezioni personalizzate e i codici di errore quando si utilizzava richieste ajax (jquery mobile nel mio caso). Ecco la soluzione che ho trovato, senza coinvolgere la sovrascrittura della modalità di debug. Genera errori personalizzati nella modalità di sviluppo e anche in modalità di produzione. Spero che aiuta qualcuno:

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

Puoi lasciare fuori Configure::read('debug') > 0 se si desidera visualizzare l'eccezione in modalità debug. La vista errorjson.ctp si trova in 'Error/errorjson.ctp ':

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

In questo caso la mia eccezione si chiama

TestException

ed è definito come segue:

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

dove ho un codice di errore personalizzato 2, $code = 2, per la mia risposta JSON. La risposta ajax lancerà un errore 500 con i seguenti dati JSON:

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

Ovviamente, è anche necessario generare l'eccezione dal vostro controller:

throw new TestException(); 

e includere il renderer eccezione http://book.cakephp.org/2.0/en/development/exceptions.html#using-a-custom-renderer-with-exception-renderer-to-handle-application-exceptions

Questo può essere un po 'fuori portata, ma per gestire la risposta all'errore ajax in JQuery io uso:

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

Funziona molto bene. Grazie. – MotsManish

7

Come menzionato sopra, le eccezioni sono il modo per restituire un errore su una richiesta AJAX in CakePHP. Ecco la mia soluzione per ottenere un controllo più preciso di come si presenta l'errore. Inoltre, come sopra, sto usando un Custom Exception Renderer, ma non un'eccezione personalizzata. La risposta di errore di default è un oggetto JSON come questo:

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

ho quasi come il modo in cui il renderer di default gestisce AJAX errori; Voglio solo modificarlo un po ':

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

Poi, in bootstrap.php:

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

: ecco come funziona:

  • dire che voglio restituire un nuovo Token CSRF nella mia risposta di errore, in modo che se il mio token esistente è scaduto prima che l'eccezione venisse lanciata, non ottengo blackholed la prossima volta che provo la richiesta. Controlla il Security Component documentation per ulteriori informazioni sulla protezione CSRF.
  • Creare una nuova classe in app/Lib/Errore. È possibile estendere il renderer predefinito o no. Dal momento che voglio solo cambiare alcune piccole cose, e per mantenere l'esempio semplice, lo sto estendendo.
  • Sovrascrivere i metodi utilizzati dal renderer predefinito per creare l'oggetto JSON che verrà restituito. Questo viene fatto tramite lo Request Handler Component e conforme alle migliori pratiche. In effetti, il renderer predefinito fa la stessa cosa.
  • Nuovo metodo privato per mantenere le cose ASCIUTTE.
  • La mia soluzione al problema di non ricevere messaggi di errore personalizzati in produzione è aggiungere una chiave di configurazione opzionale. Per impostazione predefinita, questa classe mostrerà i messaggi generici in produzione, ma se avete impostato il debug a 0, e si desidera che i messaggi di errore specifici: Configure::write('detailed_exceptions', 1);
  • Aggiungere il nuovo token per la risposta, se esiste.Nel mio caso, ho già chiamato Controller::set sul nuovo token nel metodo beforeFilter di AppController, quindi è disponibile in $this->controller->viewVars. Ci sono probabilmente dozzine di altri modi per raggiungere questo obiettivo.

Ora la vostra risposta è simile al seguente:

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

Qualsiasi dato ulteriore, di qualsiasi tipo può essere aggiunto alla matrice passato al Controller::set per lo stesso risultato.

+0

questo mi aiuta molto. –