API REST 404: URI errato o risorsa mancante?


219

Sto creando un'API REST, ma ho riscontrato un problema.

Sembra che la pratica accettata nella progettazione di un'API REST sia che se la risorsa richiesta non esiste, viene restituito un 404.

Tuttavia, per me, questo aggiunge inutili ambiguità. HTTP 404 è tradizionalmente associato a un URI errato. Quindi in effetti stiamo dicendo "O sei arrivato nel posto giusto, ma quel record specifico non esiste, o non esiste una posizione simile su Internet! Non sono davvero sicuro di quale ..."

Considera il seguente URI:

http://mywebsite/api/user/13

Se ricevo un 404 indietro, è perché l'utente 13 non esiste? O è perché il mio URL avrebbe dovuto essere:

http://mywebsite/restapi/user/13

In passato, ho appena restituito un risultato NULL con un HTTP 200 OKcodice di risposta se il record non esiste. È semplice e secondo me molto pulito, anche se non è necessariamente una pratica accettata. Ma c'è un modo migliore per farlo?



7
L'altra domanda sembra essere correlata ai formati di stringa di query URI. La discussione su 404 non è sufficiente per rispondere alla mia domanda, ovvero se esiste un modo più appropriato o utile per determinare cosa significhi effettivamente un 404. Ho esaminato quello prima di pubblicare.
Brian Lacy,

È normale quando il concole del browser consiste negli errori 404? Quando eseguo operazioni regolari con l'uri corretto ma la risorsa non è stata trovata.
Vasiliy Mazhekin,

3
404 non indica un URI errato, indica una risorsa non trovata. Ciò potrebbe essere dovuto al fatto che non vi è alcun utente "13" o potrebbe essere perché non esiste alcuna risorsa / sito Web / api.
ChrisV,

Risposte:


101

404 è solo il codice di risposta HTTP. Inoltre, puoi fornire un corpo di risposta e / o altre intestazioni con un messaggio di errore più significativo che gli sviluppatori vedranno.


7
Questo ha senso. Devo chiedermi, tuttavia, se in qualche modo si ottengono effettivamente dei benefici dalla restituzione della 404, rispetto alla restituzione di una 200 con una risposta nulla?
Brian Lacy,

15
Se si restituisce un 200 con un valore nullo, si sta andando contro la specifica HTTP. Puoi farlo, ma poi l'API non aderirà al vincolo "Interfaccia uniforme" di REST.
citare in giudizio il

5
... e il tuo corpo di risposta dovrebbe includere collegamenti ipertestuali a URI validi. Escludendo l'URI di root, e forse un segnalibro o due, i client dovrebbero sempre seguire i collegamenti forniti loro dal server. Quindi non è necessario inventare una semantica dettagliata su come hanno deciso di lavorare al di fuori del sistema.
fumanchu,

@DarrylHebbes cosa intendi, posso fare una richiesta e vedere il contenuto completo della risposta nella scheda Rete della console di sviluppo di Chrome.
Jaapz,

È incredibilmente male restituire un errore (404 o altro) per qualsiasi query che restituisce risultati vuoti. Nessun database lo farà e di solito non si desidera segnalare all'utente un risultato vuoto come condizione imprevista. Esempio: richiedi all'API di verificare se è previsto un appuntamento per i prossimi 30 minuti. Se non ce ne sono, NON si dovrebbe restituire un 404. Restituire un 200 OK e un array vuoto, per favore.
esmiralha,

59

Utilizzare 404se la risorsa non esiste. Non tornare 200con un corpo vuoto.

Questo è simile alla undefinedstringa vuota (ad es. "") Nella programmazione. Sebbene molto simile, c'è sicuramente una differenza.

404significa che non esiste nulla in quell'URI (come una variabile indefinita in programmazione). Tornare 200con un corpo vuoto significa che qualcosa esiste lì e che qualcosa è semplicemente vuoto in questo momento (come una stringa vuota in programmazione).

404non significa che sia stato un "cattivo URI". Esistono codici HTTP speciali destinati a errori URI (ad es 414 Request-URI Too Long.).


1
Ehi, penso che potresti essere coinvolto in qualcosa. Non avrebbe più senso restituire uno dei "41?" errori in caso di problemi con la risorsa richiesta? Ad esempio, 410 Andato significa "La risorsa richiesta non è più disponibile sul server e non è noto alcun indirizzo di inoltro". - (Vedi w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.11 )
Brian Lacy

In realtà, la risorsa a cui ho fatto riferimento in precedenza dice anche "Se il server non è a conoscenza o non ha la possibilità di determinare se la condizione è permanente o meno, DOVREBBE invece essere utilizzato il codice di stato 404 (Non trovato)". Quindi forse questa non è necessariamente l'opzione migliore ..
Brian Lacy,

2
Non credo che nessuno degli errori 41x sia appropriato al tuo caso d'uso. Mi piace il confronto con una stringa indefinita vs. vuota, che è una versione più concisa del mio punto nella mia risposta. C'è una differenza, ma ciò non implica che una stringa vuota sia un errore. Per allungare l'analogia: Si potrebbe avere un metodo String getName()che ha un'implementazione in questo modo: return this.name == null ? "" : this.name. Questo non è errato a meno che tu non voglia che il client sappia che lo this.nameè null.
jhericks,

1
404 è ancora l'opzione più appropriata. Puoi (e ti incoraggiamo a) utilizzare il corpo di quella risposta 404 per informare l'utente / cliente dei dettagli specifici per la ricezione dell'errore. Vedi: stackoverflow.com/a/9999335/105484 . Nel tuo caso potresti voler usare quel corpo per suggerire all'utente di riformattare il proprio URI in modo che assomigli a / restapi / user / USER_ID
nategood

Ok, posso vedere alcuni punti positivi qui, ma 4XX sono codici di errore, come è una situazione di errore? Come può il cliente impedire che accada 404?
Krzysztof Kubicki,

20

Come per la maggior parte delle cose, "dipende". Ma per me, la tua pratica non è male e non va contro le specifiche HTTP di per sé . Tuttavia, chiariamo alcune cose.

Innanzitutto, gli URI dovrebbero essere opachi. Anche se non sono opachi per le persone, sono opachi per le macchine. In altre parole, la differenza tra http://mywebsite/api/user/13, http://mywebsite/restapi/user/13è uguale alla differenza tra http://mywebsite/api/user/13e http://mywebsite/api/user/14cioè non uguale non è lo stesso periodo. Quindi un 404 sarebbe completamente appropriato per http://mywebsite/api/user/14(se non esiste tale utente) ma non necessariamente l'unica risposta appropriata.

È inoltre possibile restituire una risposta vuota 200 o più esplicitamente una risposta 204 (nessun contenuto). Ciò trasmetterebbe qualcos'altro al cliente. Implicherebbe che la risorsa identificata da http://mywebsite/api/user/14non ha contenuto o è essenzialmente nulla. Significa che v'è una tale risorsa. Tuttavia, ciò non significa necessariamente che stai sostenendo che esiste un utente persistente in un archivio dati con ID 14. Questa è la tua preoccupazione privata, non quella del cliente che effettua la richiesta. Quindi, se ha senso modellare le tue risorse in quel modo, vai avanti.

Ci sono alcune implicazioni di sicurezza nel fornire informazioni ai tuoi clienti che potrebbero rendere più facile per loro indovinare gli URI legittimi. Restituire un 200 in mancate invece di un 404 può dare al cliente un indizio che almeno la http://mywebsite/api/userparte è corretta. Un client dannoso potrebbe semplicemente continuare a provare numeri interi diversi. Ma per me, un client maligno sarebbe in grado di indovinare la http://mywebsite/api/userparte comunque. Un rimedio migliore sarebbe usare l'UUID. cioè http://mywebsite/api/user/3dd5b770-79ea-11e1-b0c4-0800200c9a66è meglio di http://mywebsite/api/user/14. In questo modo, potresti usare la tua tecnica per restituire 200 senza dare molto.


1
Questa è la soluzione che ho scelto e utilizzato con un 204 anziché un 404. Per me 204 significa "Ho trovato il tuo codice, ma non ho trovato risultati" e 404 significa "Non sono riuscito a trovare alcun codice da eseguire, è questo la strada sbagliata? "
Matt Sanders,

11

404 Not Found significa tecnicamente che uri non è attualmente associato a una risorsa. Nel tuo esempio, interpreto una richiesta http://mywebsite/api/user/13che restituisce un 404 per suggerire che questo URL non è mai stato mappato su una risorsa. Per il cliente, quella dovrebbe essere la fine della conversazione.

Per rispondere ai dubbi con ambiguità, puoi migliorare la tua API fornendo altri codici di risposta. Ad esempio, supponendo di voler consentire ai client di inviare le richieste GET all'URL http://mywebsite/api/user/13, si desidera comunicare che i client devono utilizzare l'URL canonico http://mywebsite/restapi/user/13. In tal caso, potresti prendere in considerazione l'idea di emettere un reindirizzamento permanente restituendo un 301 Spostato in modo permanente e fornendo l'URL canonico nell'intestazione Posizione della risposta. Questo dice al cliente che per future richieste dovrebbero usare l'URL canonico.


11

Quindi, in sostanza, sembra che la risposta potrebbe dipendere da come viene formata la richiesta.

Se la risorsa richiesta fa parte dell'URI come da una richiesta http://mywebsite/restapi/user/13e l'utente 13 non esiste, allora un 404 è probabilmente appropriato e intuitivo perché l'URI è rappresentativo di un utente / entità / documento / ecc. Inesistente. Lo stesso http://mywebsite/api/user/3dd5b770-79ea-11e1-b0c4-0800200c9a66vale per la tecnica più sicura usando un GUID e l'argomento api / restapi sopra.

Tuttavia, se l'ID risorsa richiesto fosse incluso nell'intestazione della richiesta [includi il tuo esempio], o in effetti, nell'URI come parametro, ad es. http://mywebsite/restapi/user/?UID=13L'URI sarebbe comunque corretto (poiché il concetto di USER esce da http://mywebsite/restapi/user/); e quindi ci si potrebbe ragionevolmente aspettare che la risposta sia un 200 (con un messaggio appropriatamente dettagliato) perché l'utente specifico noto come 13 non esiste ma l'URI esiste. In questo modo stiamo dicendo che l'URI è buono, ma la richiesta di dati non ha contenuto.

Personalmente un 200 non si sente ancora bene (anche se in precedenza l'ho sostenuto). Un codice di 200 risposte (senza una risposta dettagliata) potrebbe causare un problema da non esaminare se, ad esempio, viene inviato un ID errato.

Un approccio migliore sarebbe inviare una 204 - No Contentrisposta. Ciò è conforme alla descrizione di w3c * Il server ha soddisfatto la richiesta ma non ha bisogno di restituire un corpo-entità e potrebbe voler restituire una metainformazione aggiornata. * 1 La confusione, secondo me, è causata dalla voce di Wikipedia che afferma 204 Nessun contenuto - Il server ha elaborato correttamente la richiesta, ma non sta restituendo alcun contenuto. Solitamente utilizzato come risposta a una richiesta di eliminazione riuscita .L'ultima frase è altamente discutibile. Considera la situazione senza quella frase e la soluzione è semplice: basta inviare un 204 se l'entità non esiste. C'è anche un argomento per restituire un 204 invece di un 404, la richiesta è stata elaborata e nessun contenuto è stato restituito! Si noti che 204 non consentono il contenuto nel corpo della risposta

fonti

http://en.wikipedia.org/wiki/List_of_HTTP_status_codes 1. http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html


9

Questo è un post molto vecchio, ma ho dovuto affrontare un problema simile e vorrei condividere la mia esperienza con voi ragazzi.

Sto costruendo un'architettura microservice con API di riposo. Ho alcuni servizi GET di riposo, raccolgono dati dal sistema di back-end in base ai parametri della richiesta.

Ho seguito i restanti documenti di progettazione dell'API e ho inviato HTTP 404 con un perfetto messaggio di errore JSON al client quando non c'erano dati allineati alle condizioni della query (ad esempio è stato selezionato il record zero).

Quando non c'erano dati da rispedire al client, ho preparato un messaggio JSON perfetto con codice di errore interno, ecc. Per informare il cliente sul motivo del "Non trovato" ed è stato rispedito al client con HTTP 404. Che funziona bene.

Più tardi ho creato una classe client API di riposo che è un aiuto semplice per nascondere il codice relativo alla comunicazione HTTP e ho usato questo aiuto tutto il tempo quando ho chiamato le mie API di riposo dal mio codice.

MA avevo bisogno di scrivere codice extra confuso solo perché HTTP 404 aveva due diverse funzioni:

  • il vero HTTP 404 quando l'API di riposo non è disponibile nell'URL specificato, viene generata dal server delle applicazioni o dal server Web in cui è in esecuzione l'applicazione dell'API di riposo
  • il client recupera anche HTTP 404 quando non ci sono dati nel database in base alla condizione in cui la query.

Importante: il gestore degli errori dell'API di riposo rileva tutte le eccezioni visualizzate nel servizio back-end, il che significa che in caso di errore l'API di riposo restituisce sempre con un messaggio JSON perfetto con i dettagli del messaggio.

Questa è la prima versione del mio metodo di supporto client che gestisce le due diverse risposte HTTP 404:

public static String getSomething(final String uuid) {
    String serviceUrl = getServiceUrl();
    String path = "user/" + , uuid);
    String requestUrl = serviceUrl + path;
    String httpMethod = "GET";

    Response response = client
            .target(serviceUrl)
            .path(path)
            .request(ExtendedMediaType.APPLICATION_UTF8)
            .get();

    if (response.getStatus() == Response.Status.OK.getStatusCode()) {
        // HTTP 200
        return response.readEntity(String.class);
    } else {
        // confusing code comes here just because
        // I need to decide the type of HTTP 404...

        // trying to parse response body
        try {
            String responseBody = response.readEntity(String.class);
            ObjectMapper mapper = new ObjectMapper();
            ErrorInfo errorInfo = mapper.readValue(responseBody, ErrorInfo.class);

            // re-throw the original exception
            throw new MyException(errorInfo);

        } catch (IOException e) {
            // this is a real HTTP 404
            throw new ServiceUnavailableError(response, requestUrl, httpMethod);
        }

    // this exception will never be thrown
    throw new Exception("UNEXPECTED ERRORS, BETTER IF YOU DO NOT SEE IT IN THE LOG");
}

MA , poiché il mio client Java o JavaScript può ricevere due tipi di HTTP 404 in qualche modo ho bisogno di controllare il corpo della risposta in caso di HTTP 404. Se riesco ad analizzare il corpo della risposta, sono sicuro di aver ricevuto una risposta dove c'era nessun dato da rispedire al client.

Se non riesco ad analizzare la risposta, ciò significa che ho recuperato un HTTP 404 reale dal server Web (non dall'applicazione API restante).

È così confuso e l'applicazione client deve sempre eseguire analisi aggiuntive per verificare il vero motivo di HTTP 404.

Onestamente non mi piace questa soluzione. È fonte di confusione, deve aggiungere continuamente codice di cazzate ai clienti.

Quindi, invece di utilizzare HTTP 404 in questi due diversi scenari, ho deciso di fare quanto segue:

  • Non sto più usando HTTP 404 come codice HTTP di risposta nella mia applicazione di riposo.
  • Userò HTTP 204 (nessun contenuto) invece di HTTP 404.

In tal caso il codice client può essere più elegante:

public static String getString(final String processId, final String key) {
    String serviceUrl = getServiceUrl();
    String path = String.format("key/%s", key);
    String requestUrl = serviceUrl + path;
    String httpMethod = "GET";

    log(requestUrl);

    Response response = client
            .target(serviceUrl)
            .path(path)
            .request(ExtendedMediaType.APPLICATION_JSON_UTF8)
            .header(CustomHttpHeader.PROCESS_ID, processId)
            .get();

    if (response.getStatus() == Response.Status.OK.getStatusCode()) {
        return response.readEntity(String.class);
    } else {
        String body = response.readEntity(String.class);
        ObjectMapper mapper = new ObjectMapper();
        ErrorInfo errorInfo = mapper.readValue(body, ErrorInfo.class);
        throw new MyException(errorInfo);
    }

    throw new AnyServerError(response, requestUrl, httpMethod);
}

Penso che questo gestisca meglio questo problema.

Se hai una soluzione migliore, condividila con noi.


I metodi Apache HttpClient non generano eccezioni su una risposta 204. Se il tuo client risponde solo a oggetti business (modelli), questo restituirà null. Funziona, ma è difficile per gli utenti finali rilevare la situazione 204.
chrisinmtown,

Va tutto bene, ma una domanda è: quante volte scrivi se le dichiarazioni per il 404? E quale sarebbe? D'altra parte, se si effettua una chiamata all'API, che definisce il possibile 404 con l'errore json all'interno, è possibile gestirlo solo per questo caso.
Max

zappee sono d'accordo con te. Ho un servizio che restituisce dati. Ma ci sono situazioni in cui i dati sono rotti. L'URL della richiesta è corretto (quindi non un 404), ma in realtà non è un errore logico (500), quindi 204 sembra il più adatto. È davvero fastidioso per la mancanza di un messaggio sul corpo del messaggio. Sebbene probabilmente potrei inserire un motivo in un'intestazione.
cs94njw,


1

L'identificatore di risorsa uniforme è un puntatore univoco alla risorsa. Un URI mal formato non punta alla risorsa e quindi eseguendo un GET su di essa non restituirà una risorsa. 404 significa che il server non ha trovato nulla corrispondente all'URI di richiesta. Se inserisci un URI errato o un URI errato, questo è il tuo problema e il motivo per cui non sei arrivato a una risorsa se una pagina HTML o IMG.


0

Per questo scenario HTTP 404 è il codice di risposta per la risposta dall'API REST come 400, 401, 404, 422 entità non elaborabile

utilizzare la gestione delle eccezioni per controllare il messaggio di eccezione completo.

try{
  // call the rest api
} catch(RestClientException e) {
     //process exception
     if(e instanceof HttpStatusCodeException){
        String responseText=((HttpStatusCodeException)e).getResponseBodyAsString();
         //now you have the response, construct json from it, and extract the errors
         System.out.println("Exception :" +responseText);
     }

}

Questo blocco di eccezione fornisce il messaggio corretto generato dall'API REST

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.