Qual è il modo migliore per creare un modello di risposta agli errori API REST e un sistema di codici di errore?


15

La mia implementazione REST restituirà errori in JSON con la struttura successiva:

{
 "http_response":400,
 "dev_message":"There is a problem",
 "message_for_user":"Bad request",
 "some_internal_error_code":12345
}

Suggerisco di creare un modello di risposta speciale, in cui posso trasmettere i valori necessari per le proprietà (dev_message, message_for_user, some_internal_error_code) e restituirli. Nel codice sarebbe simile a questo:

$responseModel = new MyResponseModel(400,"Something is bad", etc...);

Come dovrebbe essere questo modello? Dovrei implementare metodi, ad esempio successResponse (), dove passerò solo informazioni di testo e il codice sarà predefinito 200 lì? Sono bloccato con questo. E questa è la prima parte della mia domanda: devo implementare questo modello, è questa buona pratica? Perché per ora, sto solo restituendo array direttamente dal codice.

La seconda parte riguarda il sistema dei codici di errore. I codici di errore saranno descritti nella documentazione. Ma il problema che sto riscontrando è nel codice. Qual è il modo migliore per gestire i codici di errore? Devo scriverli all'interno del modello? O sarebbe meglio creare un servizio separato per gestirlo?

AGGIORNAMENTO 1

Ho implementato la classe del modello per la risposta. È simile alla risposta di Greg, la stessa logica, ma in più ho codificato errori scritti nel modello ed ecco come appare:

    class ErrorResponse
    {
     const SOME_ENTITY_NOT_FOUND = 100;
     protected $errorMessages = [100 => ["error_message" => "That entity doesn't exist!"]];

     ...some code...
    }

Perché l'ho fatto? E per cosa?

  1. È bello nel codice: return new ErrorResponse(ErrorResponse::SOME_ENTITY_NOT_FOUND );
  2. Messaggio di errore facile da cambiare. Tutti i messaggi si trovano in un unico posto anziché nel controller / servizio / ecc. O in qualunque altra cosa tu lo abbia inserito.

Se hai suggerimenti per migliorare, per favore, commenta.

Risposte:


13

In questa situazione, penso sempre prima all'interfaccia, quindi scrivo il codice PHP per supportarlo.

  1. È un'API REST, quindi sono indispensabili codici di stato HTTP significativi.
  2. Desideri che strutture di dati coerenti e flessibili vengano inviate da e verso il client.

Pensiamo a tutte le cose che potrebbero andare storte e ai loro codici di stato HTTP:

  • Il server genera un errore (500)
  • Errore di autenticazione (401)
  • La risorsa richiesta non è stata trovata (404)
  • I dati che stai modificando sono stati modificati da quando li hai caricati (409)
  • Errori di convalida durante il salvataggio dei dati (422)
  • Il cliente ha superato il tasso di richiesta (429)
  • Tipo di file non supportato (415)

Nota, ce ne sono altri che puoi cercare in seguito.

Per la maggior parte delle condizioni di errore, è necessario restituire un solo messaggio di errore. Il422 Unprocessable Entity risposta, che ho usato per "errori di convalida", potrebbe restituire più di un errore --- Uno o più errori per campo modulo.

Abbiamo bisogno di una struttura dati flessibile per le risposte agli errori.

Prendiamo ad esempio il 500 Internal Server Error:

HTTP/1.1 500 Internal Server Error
Content-Type: text/json
Date: Fri, 16 Jan 2015 17:44:25 GMT
... other headers omitted ...

{
    "errors": {
        "general": [
            "Something went catastrophically wrong on the server! BWOOP! BWOOP! BWOOP!"
        ]
    }
}

Contrastalo con semplici errori di convalida quando provi a POST qualcosa sul server:

HTTP/1.1 422 Unprocessable Entity
Content-Type: text/json
Date: Fri, 16 Jan 2015 17:44:25 GMT
... other headers omitted ...

{
    "errors": {
        "first_name": [
            "is required"
        ],
        "telephone": [
            "should not exceed 12 characters",
            "is not in the correct format"
        ]
    }
}

La chiave qui è il tipo di contenuto text/json. Questo dice alle applicazioni client che possono decodificare il corpo della risposta con un decoder JSON. Se, ad esempio, non viene rilevato un errore interno del server e viene invece consegnata la pagina Web generica "Qualcosa è andato storto", il tipo di contenuto dovrebbe esseretext/html; charset=utf-8 tale che le applicazioni client non tentino di decodificare il corpo della risposta come JSON.

Questo sembra tutto trovare e dandy, fino a quando non è necessario supportare le risposte JSONP . È necessario restituire una 200 OKrisposta, anche per errori. In questo caso dovrai rilevare che il client richiede una risposta JSONP (in genere rilevando un parametro di richiesta URL chiamatocallback ) e modificare un po 'la struttura dei dati:

(GET / posts / 123? Callback = displayBlogPost)

<script type="text/javascript" src="/posts/123?callback=displayBlogPost"></script>

HTTP/1.1 200 OK
Content-Type: text/javascript
Date: Fri, 16 Jan 2015 17:44:25 GMT
... other headers omitted ...

displayBlogPost({
    "status": 500,
    "data": {
        "errors": {
            "general": [
                "Something went catastrophically wrong on the server! BWOOP! BWOOP! BWOOP!"
            ]
        }
    }
});

Quindi il gestore risposte sul client (in un browser Web) dovrebbe avere una funzione JavaScript globale chiamata displayBlogPostche accetta un singolo argomento. Questa funzione dovrebbe determinare se la risposta ha avuto esito positivo:

function displayBlogPost(response) {
    if (response.status == 500) {
        alert(response.data.errors.general[0]);
    }
}

Quindi ci siamo presi cura del cliente. Ora prendiamoci cura del server.

<?php

class ResponseError
{
    const STATUS_INTERNAL_SERVER_ERROR = 500;
    const STATUS_UNPROCESSABLE_ENTITY = 422;

    private $status;
    private $messages;

    public function ResponseError($status, $message = null)
    {
        $this->status = $status;

        if (isset($message)) {
            $this->messages = array(
                'general' => array($message)
            );
        } else {
            $this->messages = array();
        }
    }

    public function addMessage($key, $message)
    {
        if (!isset($message)) {
            $message = $key;
            $key = 'general';
        }

        if (!isset($this->messages[$key])) {
            $this->messages[$key] = array();
        }

        $this->messages[$key][] = $message;
    }

    public function getMessages()
    {
        return $this->messages;
    }

    public function getStatus()
    {
        return $this->status;
    }
}

E per usarlo in caso di un errore del server:

try {
    // some code that throws an exception
}
catch (Exception $ex) {
    return new ResponseError(ResponseError::STATUS_INTERNAL_SERVER_ERROR, $ex->message);
}

O durante la convalida dell'input dell'utente:

// Validate some input from the user, and it is invalid:

$response = new ResponseError(ResponseError::STATUS_UNPROCESSABLE_ENTITY);
$response->addMessage('first_name', 'is required');
$response->addMessage('telephone', 'should not exceed 12 characters');
$response->addMessage('telephone', 'is not in the correct format');

return $response;

Successivamente, hai solo bisogno di qualcosa che prenda l'oggetto di risposta restituito e lo converta in JSON e invia la risposta nel modo giusto.


Grazie per una risposta! Ho implementato una soluzione simile. L'unica differenza che non trasmetto messaggi da solo, sono già impostati (vedi la mia domanda aggiornata).
Grokking

-2

Stavo affrontando qualcosa di simile, ho fatto 3 cose,

  1. Ho creato un ExceptionHandler per me chiamato ABCException.

Dal momento che sto usando Java e Spring,

L'ho definito come

 public class ABCException extends Exception {
private String errorMessage;
private HttpStatus statusCode;

    public ABCException(String errorMessage,HttpStatus statusCode){
            super(errorMessage);
            this.statusCode = statusCode;

        }
    }

Quindi lo ha chiamato ovunque richiesto, in questo modo,

throw new ABCException("Invalid User",HttpStatus.CONFLICT);

E sì, è necessario creare un ExceptionHandler nel controller se si utilizza un servizio Web basato su REST.

Annota con @ExceptionHandlerse usi Spring


I programmatori riguardano domande e risposte concettuali che dovrebbero spiegare le cose . Lanciare i dump del codice invece della spiegazione è come copiare il codice dall'IDE alla lavagna: può sembrare familiare e persino a volte comprensibile, ma sembra strano ... solo strano. La lavagna non ha compilatore
moscerino del
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.