Qual è il modo migliore per creare il modello di risposta all'errore dell'API REST e il sistema dei codici di errore?

15

La mia implementazione REST restituirà errori in JSON con 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 passare 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? Devo implementare i metodi, ad es. successResponse () dove passerò solo le 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 gli array direttamente dal codice.

La seconda parte riguarda il sistema del codice di errore. I codici di errore saranno descritti nella documentazione. Ma il problema che sto incontrando è 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?

UPDATE 1

Ho implementato la classe del modello per la risposta. È simile alla risposta di Greg, alla stessa logica, ma in aggiunta ho errori scritti hardcoded 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. Sembra interessante nel codice: %codice%
  2. Facile da cambiare messaggio di errore. Tutti i messaggi sono in un posto al posto del controller / servizio / etc o qualunque cosa tu abbia inserito.

Se hai suggerimenti per migliorare questo, per favore, commenta.

    
posta Grokking 16.01.2015 - 10:41
fonte

2 risposte

13

In questa situazione, penso sempre all'interfaccia, quindi scrivo codice PHP per supportarla.

  1. È un'API REST, quindi i codici di stato HTTP significativi sono indispensabili.
  2. Desideri che strutture di dati coerenti e flessibili vengano inviate al e dal 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 lo hai caricato (409)
  • Errori di convalida durante il salvataggio dei dati (422)
  • Il client ha superato il tasso di richiesta (429)
  • Tipo di file non supportato (415)

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

Per la maggior parte delle condizioni di errore, è necessario restituire un solo messaggio di errore. La risposta 422 Unprocessable Entity , che ho utilizzato 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 di errore.

Prendi come esempio, 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!"
        ]
    }
}

Confrontalo con semplici errori di convalida quando provi a pubblicare 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 che è text/json . Questo dice alle applicazioni client che possono decodificare il corpo della risposta con un decodificatore JSON. Se, ad esempio, non viene rilevato un errore interno del server e la tua pagina Web generica "Qualcosa è andato storto" viene consegnata, il tipo di contenuto dovrebbe essere text/html; charset=utf-8 in modo che le applicazioni client non tenteranno di decodificare il corpo della risposta come JSON.

Questo sembra tutto trovare e dandy, finché non è necessario supportare le JSONP risposte. È necessario restituire una risposta 200 OK , anche per gli errori. In questo caso dovrai rilevare che il client richiede una risposta JSONP (in genere rilevando un parametro di richiesta URL chiamato callback ) e modifica un po 'la struttura 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 di risposta sul client (in un browser Web) dovrebbe avere una funzione JavaScript globale chiamata displayBlogPost che accetta un singolo argomento. Questa funzione dovrebbe determinare se la risposta ha avuto successo:

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 usare questo nel 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);
}

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

Dopodiché, hai solo bisogno di qualcosa che prenda l'oggetto risposta restituito e lo converta in JSON e manda la risposta a modo suo.

    
risposta data 16.01.2015 - 19:13
fonte
-2

Ero di fronte a qualcosa di simile, ho fatto 3 cose,

  1. Creato un ExceptionHandler per me chiamato ABCException.

Dato che sto usando Java & Primavera,

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;

        }
    }

Poi lo ha chiamato ovunque richiesto, come questo,

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

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

Annotalo con @ExceptionHandler se usi Spring

    
risposta data 16.01.2015 - 11:47
fonte

Leggi altre domande sui tag