Un'API REST dovrebbe restituire un errore del server interno 500 per indicare che una query fa riferimento a un oggetto che non esiste?


39

Sto lavorando con un'API REST che risiede su un server che gestisce i dati per una moltitudine di dispositivi IoT.

Il mio compito è interrogare il server utilizzando l'API per raccogliere informazioni specifiche sulle prestazioni di tali dispositivi.

In un caso, ottengo un elenco dei dispositivi disponibili e dei relativi identificatori, quindi in seguito eseguo una query sul server per ulteriori dettagli utilizzando tali identificatori (GUID).

Il server sta restituendo un 500 Internal Server Errorper una query su uno di quegli ID. Nella mia applicazione, viene generata un'eccezione e non vedo i dettagli sull'errore. Se esamino la risposta più da vicino con Postman , vedo che il server ha restituito JSON nel corpo che contiene:

errorMessage: "This ID does not exist".

Ignora il fatto che il server ha fornito l'ID per cominciare - questo è un problema separato per lo sviluppatore.

Un'API REST dovrebbe restituire a 500 Internal Server Errorper segnalare che una query fa riferimento a un oggetto che non esiste? Secondo me, i codici di risposta HTTP dovrebbero riferirsi rigorosamente allo stato della chiamata REST, piuttosto che alla meccanica interna dell'API. Mi aspetterei un 200 OKcon la risposta contenente l'errore e la descrizione, che sarebbero proprietari dell'API in questione.


Mi viene in mente che esiste una potenziale differenza di aspettativa a seconda di come è strutturata la chiamata REST.

Considera questi esempi:

  1. http://example.com/restapi/deviceinfo?id=123
  2. http://example.com/restapi/device/123/info

Nel primo caso, l'ID dispositivo viene passato come variabile GET. Un 404 o 500 indicherebbe che il percorso ( /restapi/deviceinfo) non è stato trovato o ha provocato un errore del server.

Nel secondo caso, l'ID dispositivo fa parte dell'URL. Sarei più comprensivo di un 404 Not Found, ma potrei ancora discutere sulla base di quali parti del percorso sono interpretate come variabili rispetto a endpoint.


Questa condizione che stai descrivendo è considerata un successo o un fallimento?
Robert Harvey,


@RobertHarvey La mia aspettativa era che l'API avrebbe restituito alcune informazioni sul dispositivo. La query stessa avrebbe dovuto avere esito positivo, ma l'ID dispositivo scomparso non dovrebbe causare un errore a livello di richiesta.
JYelton,

18
Un ID inesistente suona come un 404 per me. Il motivo più comune per 500 errori è rappresentato dai programmatori che consentono l'esclusione di un'eccezione interna da gestire per il server di hosting. A volte ci sono casi davvero eccezionali in cui ciò accade e talvolta è solo una programmazione pigra. Difficile dire che sta succedendo qui.
Berin Loritsch,

9
500 server interno significa "È colpa nostra, abbiamo incasinato qualcosa" e non dovresti mai mirare a restituire questo stato a un utente. Il suo scopo è sostanzialmente quello di indicare un bug: il tuo utente può dire che ottiene una 500 quando fa una richiesta particolare, quindi puoi andare e risolverlo.
berry120,

Risposte:


96

Penso che una risposta 404 sia la migliore corrispondenza semantica qui, perché la risorsa che stavi cercando di trovare (come rappresentato dall'URI utilizzato per la query) non è stata trovata. Restituire un payload di errore nel corpo è ragionevole, ma non richiesto.

Secondo RFC 2616 , la definizione del codice di stato 404 è:

10.4.5 404 non trovato
Il server non ha trovato nulla che corrisponda all'URI di richiesta. Non viene fornita alcuna indicazione se la condizione è temporanea o permanente. Il codice di stato 410 (andato) DOVREBBE essere utilizzato se il server è a conoscenza, tramite un meccanismo configurabile internamente, che una vecchia risorsa è permanentemente non disponibile e non ha un indirizzo di inoltro. Questo codice di stato viene comunemente utilizzato quando il server non desidera rivelare esattamente perché la richiesta è stata rifiutata o quando non è applicabile alcuna altra risposta.


6
404 è una corrispondenza semantica solo se l'id che non esiste corrisponde alla risorsa che viene recuperata.
Robert Harvey,

55
@ThomasOwens Una query che non restituisce risultati restituirebbe 200 stati e una matrice vuota di risultati. Un 404 verrebbe restituito solo quando si specifica un ID oggetto specifico che in realtà non esiste.
Sean Burton,

11
@ThomasOwens: dal punto di vista del chiamante, l'endpoint non lo è /questions, lo è /questions/368213. Tale endpoint non esiste nel tuo scenario. Pensala in questo modo: se fai un GET su / foo / bar e non c'è barra, perché la risposta dovrebbe essere diversa se / foo esiste o no?
Bryan Oakley,

17
Bene, non è un errore del server, quindi non inviamo un 5xx. È un errore del client - il client ha chiesto qualcosa che non ha, quindi inviamo 4xx, in particolare 404.
bdsl

7
@ThomasOwens: How do you differentiate between a 404 meaning "the query returned no results" and a 404 meaning "the endpoint does not exist"?- 400 Richiesta non valida .
Robert Harvey,

34

Userò i tuoi esempi.

http://example.com/restapi/deviceinfo?id=123

Se l'endpoint restituisce un array json , la scelta migliore è 200 OKcon un array vuoto se non viene trovato alcun risultato.

Se l'endpoint è stato progettato per restituire un unico risultato , la mia scelta sarebbe 404 NOT FOUND, perché, per me, la sintassi giusta per questo tipo di endpoint è: http://example.com/restapi/deviceinfo/123. Di solito uso il parametro request solo per il filtro e quando il mio endpoint restituisce un array.

http://example.com/restapi/device/123/info

Penso che questa domanda abbia già ricevuto risposta qui . POST o GET, la scelta migliore sembra 404 NOT FOUNDperché la risorsa 123non è stata trovata.

In entrambi i casi non riesco a vedere la necessità di spiegare il motivo per cui la richiesta non è stata completata. Le informazioni sulla richiesta e il codice HTTP spiegano già perché.


2
Come si fa a distinguere tra un ID inesistente e un URL non valido?
Robert Harvey,

2
@luizfzs, puoi anche quello, non riesco a vedere un problema su questo. In alcune API che ho sviluppato, preferisco usare 204 in un PUT o DELETE di successo ( come spiegato qui ) senza alcuna risposta.
Dherik,

10
Perché dovresti distinguere tra un ID inesistente e un URL non valido, a quel livello? Ad ogni modo, stai ancora facendo una richiesta valida, per quanto riguarda HTTP. Il server ... beh ... non riesce a trovare la risorsa che stai indirizzando. Un URL non valido non è una richiesta non valida; è una richiesta ben formata per la cosa sbagliata .
cHao,

6
@cHao Perché come sviluppatore, voglio sapere se la mia app non funziona perché l'articolo non esiste o se ho indirizzato la mia app su app.company.cxm / test / anziché app.company.cxm / tst /
Patrick M,

7
@PatrickM: come sviluppatore, dovresti sapere che anche le risposte all'errore possono contenere un corpo di messaggio. Ecco dove vanno le informazioni esplicative sull'errore, se lo vuoi davvero. Non rompere la semantica solo perché sei paranoico sulle dita grasse. A livello HTTP, non importa se hai incasinato come /itms/1234o /items/12234.
cHao,

27

HTTP 404 è corretto, perché il server comprende quale risorsa sta chiedendo il client, ma non ha quella risorsa.

Il fatto che stai lavorando con una "API REST" è la chiave. L'API dovrebbe comportarsi come se stesse eseguendo un trasferimento di stato rappresentativo, non eseguendo una funzione. (Certo, il termine "REST" ha avuto un significato più ampio, ma è comunque possibile utilizzare il suo significato letterale con buoni risultati qui.) Il client ha richiesto lo stato di una risorsa descritta dall'URL http://example.com/restapi/device/123/info. Una querystring ( /deviceinfo?id=123) non cambierebbe la situazione. Il server sa che stai chiedendo di trasferire lo stato del dispositivo 123, ma non lo riconosce come risorsa nota. Quindi HTTP 404.

Le altre possibili risposte qui discusse hanno anche significati specifici:

  • HTTP 200- Abbiamo lo stato per te; è nel corpo della risposta.
  • HTTP 204- Abbiamo lo stato per te; è vuoto.
  • HTTP 400- Non possiamo dire di quale risorsa stai chiedendo. Correggi il tuo URL.
  • HTTP 500- Abbiamo funzionato male. Non è colpa tua.

Vedi RFC 2616 sec. 10 a seconda dei casi.


Sono d'accordo con la tua opinione su questo. Se il server restituisse un 404, ciò avrebbe più senso di quello che sta facendo (un 500). Grazie.
JYelton,

11

Un errore 5xx viene in genere utilizzato per indicare che il server ha riscontrato un errore e non può completare la richiesta. Se il server accetta la richiesta, può analizzarla correttamente e quindi funziona, ciò non dovrebbe restituire un errore 5xx.

Non sono sicuro che ci sia qualche tipo di convenzione su cosa restituire se una query non produce risultati. Ho visto sia quello che descrivi (un 200 con un corpo che contiene un messaggio) sia un 404 che indica che i risultati non sono stati trovati. Il 200 probabilmente ha più senso: la richiesta è stata completata correttamente e non si sono verificati problemi con la richiesta del client o durante l'elaborazione della richiesta da parte del server. Un corpo può recapitare un messaggio al cliente.


Tratterei entrambi i tuoi esempi ( http://example.com/restapi/deviceinfo?id=123e http://example.com/restapi/device/123/info) lo stesso - 123è un parametro. Entrambi i casi sono modi diversi di strutturare una richiesta per ottenere informazioni sul dispositivo per un dispositivo con ID 123.

Innanzitutto, prenderei in considerazione l'autorizzazione e l'autenticazione. Se l'utente non dispone delle autorizzazioni appropriate, restituirei un 403 o un 401 come appropriato. Anche se si chiama "Non autorizzato", la mia comprensione è che il 401 riguarda maggiormente l'autenticazione e il 403 riguarda il non autorizzato o l'autorizzazione negata. Non sarei troppo esigente qui, però, se volessi semplicemente attenersi al 403 per tutti gli errori di autenticazione e autorizzazione.

Quindi, vorrei gestire l'ID. Sulla base del tuo esempio, sembra che sia un ID numerico. Se fosse stato fornito un valore non numerico, restituirei 400. Se il parametro potesse essere un identificatore di dispositivo valido, continuerei con l'elaborazione. Se ci fossero altri argomenti, verrebbero controllati anche qui. Mi aspetterei che l'organismo di risposta contenga informazioni appropriate sul motivo per cui la richiesta è stata negativa.

Se tutti i parametri fossero validi, inizierei a elaborare la richiesta. Se il sistema o qualsiasi dipendenza (un database, un servizio di terze parti, un altro servizio interno) non fosse disponibile, restituirei un codice 5xx - 503 sarebbe specifico, ma sarebbe accettabile anche un 500. In entrambi i casi, restituirei un corpo con ulteriori dettagli. Considera che se una dipendenza esterna segnala un timeout di richiesta 408, lo mangerei e restituirei un 500 al mio client, consentendo a un client di ricevere un 408 solo se la richiesta al mio sistema è scaduta. Se il sistema è in grado di completare la richiesta, restituirei 200 e l'organismo appropriato.

204 può essere utile in alcuni casi, ma ti impedisce di inviare un corpo di risposta. Soprattutto in un'impostazione API, l'invio di un corpo di risposta con informazioni che possono essere inserite in un meccanismo di registrazione o segnalazione sembra nella maggior parte dei casi la decisione giusta da prendere.

L'unica volta che verrà restituito un 404 è se il server non aveva un /deviceinfoendpoint o un /device/:id/infoendpoint.

Non prenderei in considerazione un ID che non si trova uguale alla risorsa non trovata. La risorsa sono le informazioni sul dispositivo per un dispositivo particolare (nel tuo esempio). Restituire un 404 significherebbe che la risorsa (le informazioni sul dispositivo) non esiste. Un 200 con un corpo appropriato significa che il sistema può effettivamente fornire informazioni sul dispositivo. Potrebbe esserci o meno un dispositivo con l'ID specificato.


1
@RobertHarvey Sì. Non ci sono stati errori. Solo perché il server ha generato il GUID e lo ha inviato al client non significa che un'altra operazione lo abbia rimosso. La richiesta / risposta ha avuto esito positivo. Il comando o la query rappresentata dalla richiesta non ha restituito dati. Per me non è un fallimento. Si potrebbe essere un'eccezione, ma io non sono convinto che è degno di un 5xx.
Thomas Owens

2
Se stavo progettando questa API che sto interrogando, vorrei che restituisse 200 con un corpo che quindi contiene qualcosa per l'effetto device: 123; status: not found;. Quindi so che la query ha funzionato e che l'API mi sta dicendo informazioni utili.
JYelton,

7
@Blrfl Questo è supportato (anche se non "provato") dalla formulazione di molti siti Web di 500 pagine, in genere qualcosa come "Abbiamo fatto un errore e indagherò". Nella mia mente un server che funziona correttamente non manda mai 500, tranne forse nel caso dei raggi cosmici.
martedì

6
Http non ha un concetto di "endpoint", nessuna distinzione rilevante tra una variabile e il resto dell'URL, e quindi non si riposa. Potresti avere un sistema di routing che corrisponde a una regex e instrada migliaia di URL a una funzione del controller, ma questo è un dettaglio di implementazione, non una parte fondamentale dell'API. Una risposta 200 su un get è solo per il caso in cui la risorsa richiesta viene recuperata. In questo caso il client desidera una rappresentazione di una risorsa che non esiste, quindi 404 è la risposta corretta.
bdsl

8
Un sistema correttamente funzionante non invia mai una risposta 500. Un server correttamente funzionante può inviare un 500 perché se fa parte di un sistema più grande e ha rilevato un errore interno a quel sistema più grande, ad esempio il database è inattivo, o altri servizi Web dipendenti restituiscono risultati senza senso.
bdsl

5

Un errore HTTP serie 500 indica un malfunzionamento del server. A parte 501 Not Implementede 505 HTTP Version Not Supported, l'utilizzo di questi codici di errore comporta l'implicazione che il tentativo di ripetere la richiesta in un secondo momento potrebbe avere esito positivo (anche se lo 503 Service Unavailableafferma solo esplicitamente). Idealmente, un server non dovrebbe mai produrre uno di questi codici, anche se l'incapacità di scrivere software privo di bug e fornire al server risorse infinite significa che ne avrai bisogno di tanto in tanto.

Per un risultato "l'oggetto non esiste", è necessario restituire probabilmente 404 Not Found(quando la richiesta è per un oggetto per nome) o 200 Successcon un corpo del risultato vuoto (durante la ricerca di un oggetto per attributi). 204 No Contentsembra allettante, ma lo userei solo per le situazioni in cui la mancanza di un corpo di risposta è il risultato atteso.


1

Come cliente della tua API, quando eseguo una di queste chiamate:

  1. http://example.com/restapi/deviceinfo?id=123
  2. http://example.com/restapi/device/123/info

Mi aspetto di recuperare una (rappresentazione di) un DeviceInfooggetto (o comunque un tipo particolare, che si tratti di un tipo formale o solo di qualcosa conforme a una convenzione "tipo anatra" documentata). Voglio che uno 200stato significhi che ne ho effettivamente uno e posso andare avanti e usarlo.

Per le API REST, penso ai codici di stato 400 e 500 come a delle eccezioni. Li usi per indicare quando non puoi restituire una risposta "normale" per la richiesta che hai ricevuto, quindi il cliente dovrà fare qualcosa di eccezionale piuttosto che elaborare le informazioni che si aspettava di recuperare.

Ciò significa che, come consumatore dell'API, posso usare una sorta di funzione di chiamata di riposo controllata che recupera una risposta o genera un'eccezione. È fantastico; la mia logica normale può essere un codice lineare e posso organizzare la mia gestione degli errori nello stesso modo in cui lo faccio nel codice locale. I 404 imprevisti si manifesteranno come eccezioni "nessuna risorsa trovata" senza che io debba fare nulla, non come errori di "attributo mancante" quando in seguito elaborerò { errorMessage: "Device 123 not found" }come se fosse un DeviceInfooggetto.

Se si ritiene che l'endpoint http://example.com/restapi/deviceinfo venga trovato ed è solo quello id=123che non lo è, quindi si restituisce 200 con un messaggio di errore nel corpo, quindi si stanno creando esattamente lo stesso tipo di problemi di interfaccia delle funzioni C che potrebbero restituire un risultato corretto o un codice di errore o metodi che indicano problemi con la restituzione arbitrarianull. È molto più bello come utente della tua interfaccia avere errori indicati attraverso un canale "separato" dai ritorni regolari. Questo vale anche qui, anche se le risposte HTTP 200, 404 e 500 sono lo stesso canale da un punto di vista di basso livello. Sono standardizzati e facili da distinguere in modo che il mio framework client REST possa facilmente attivare quegli stati per trasformarli nelle strutture appropriate nella mia lingua; per fare la stessa cosa con il livello JSON (dove dici sempre 200 e mi dai DeviceInfoun messaggio di errore o), devo incorporare alcune conoscenze sugli schemi JSON che usi.

Quindi utilizza 200 solo quando puoi restituire un valore valido del tipo previsto (motivo per cui http://example.com/restapi/search-devices?colour=bluepuò restituire 200 con un array vuoto se non ci sono dispositivi blu; un array vuoto è un array valido e una risposta ragionevole alla richiesta "I vorrebbe i dettagli di tutti i dispositivi blu "). Se non è possibile, utilizzare il codice di stato non 200 più appropriato. Anche se "il dispositivo 123 non esiste" è la risposta corretta a "dammi i dettagli del dispositivo 123" e non è un errore per il server , è un'eccezione per l'aspettativa del cliente che tornino indietro DeviceInfoe dovrebbero non essere comunicato come una normale risposta "ecco cosa hai chiesto".


Purtroppo sono anche cliente di questa API e non posso cambiarla. Questo è un ottimo riassunto di come dovrebbe funzionare, però.
JYelton,

0

È possibile utilizzare Errore 422 entità non elaborabile per differenziare da 404 non trovato . 422 significa che il server comprende la richiesta ma non può fornire una risposta adeguata. Sto usando questo codice in situazioni simili.


4
Questo articolo descrive l'utilizzo di 422 in maggiore dettaglio. Non credo che questo codice di errore sia davvero applicabile qui.
Robert Harvey,

Grazie, molto utile! Dovrò approfondire questo argomento!
FiNeX,

0

L'errore 500 indica normalmente che la richiesta ha avuto un arresto anomalo di un programma lato server; negli ambienti aziendali questi errori vengono trattati come un uovo in faccia e vengono evitati.

L'errore 4xx dovrebbe segnalare al programmatore che l'endpoint API (risorsa) non esiste. Di conseguenza, una volta raggiunto l'endpoint appropriato, qualsiasi gestione degli errori da quel momento in poi è responsabilità del programmatore dell'API che deve essere gestita con garbo, cioè con un messaggio di errore di una risposta 200.


1
Quindi stai dicendo "non usare 500 perché non stai bloccando il server?"
Robert Harvey,

0

Sebbene il w3 noti che 404 viene utilizzato quando non è applicabile alcuna altra risposta , non sarebbe questo a che serve una risposta 204 (nessun contenuto)? La richiesta era valida dal punto di vista dell'elaborazione e il server ha elaborato la richiesta e ha un risultato. Questo è un successo, che tende verso 2xx risposte. Non c'erano contenuti per questa particolare richiesta, quindi 204 dice all'utente che non c'era niente di sbagliato nella loro query ma non c'è nulla lì.

Potresti anche fare in modo che 409 (conflitto) sia una risposta appropriata. Sebbene 409 sia spesso usato per un POST, lo dice

Impossibile completare la richiesta a causa di un conflitto con lo stato corrente della risorsa. Questo codice è consentito solo nelle situazioni in cui è previsto che l'utente possa essere in grado di risolvere il conflitto e inviare nuovamente la richiesta. Il corpo della risposta DOVREBBE includere informazioni sufficienti affinché l'utente possa riconoscere l'origine del conflitto. Idealmente, l'entità di risposta dovrebbe includere informazioni sufficienti per l'utente o l'agente utente per risolvere il problema; tuttavia, ciò potrebbe non essere possibile e non è necessario.

probabilmente la non esistenza dell'id richiesto è il conflitto qui, e restituendo nel messaggio che tale id non esiste nel sistema è sufficiente informazione per l'utente per riconoscere e risolvere il conflitto.


3
No, 204 risposta su una richiesta get avrebbe senso solo quando la risorsa richiesta è stata trovata e la sua rappresentazione è lunga esattamente zero caratteri.
bdsl

0

Le altre risposte lo riguardano, ma volevo solo sottolineare che un errore della serie 500 significa "Ho sbagliato". In questo caso, "I" indica l'API, quindi un errore del server non gestito o simile. Un errore serie 400 significa "hai incasinato" - il chiamante dell'API ha inviato qualcosa di errato.


-4

Altri utenti hanno fornito risposte valide su cosa fare se si desidera leggere il libro.

Tuttavia, suggerirei che stai leggendo troppo nel RESTO e di essere assolutamente kosher rispetto ad esso.

REST è un protocollo capriccioso, perché se si desidera seguire il libro mentre lo si utilizza, costringe a creare client e server con una conoscenza specifica del fatto che stanno comunicando tramite REST, quindi in sostanza lo specifico protocollo di comunicazione è usato non può essere sottratto.

Esiste un approccio diverso: evitare completamente RESTfulness e usarlo solo come protocollo di comunicazione e nient'altro. Ciò significa che le uniche risposte che devono essere restituite sono "HTTP 200 OK" e "Errore del server interno HTTP 500" perché, per quanto riguarda il protocollo di comunicazione, qualsiasi tentativo di comunicazione può avere solo due risultati: o la richiesta è andata a buon fine recapitato al server o no.

Ciò che accade dopo la consegna non è affare del protocollo di comunicazione. Pertanto, una volta che il server ha ricevuto correttamente la richiesta e inizia a elaborarla, possono verificarsi molti errori, ma questi sono tutti errori specifici dell'applicazione che non rientrano nelle attività del protocollo di comunicazione. Dovrebbero essere comunicati nel payload di una risposta che sembra perfettamente riuscita per quanto ne sa il protocollo di comunicazione.

Quindi, in sostanza, consiglierei di restituire un "HTTP 200 OK" e all'interno del payload di risposta ("corpo del contenuto della risposta" in linguaggio HTTP) avere un codice di errore specifico dell'applicazione che dice "ID non trovato" o altro.


A giudicare dai voti negativi, credo di aver ferito i sentimenti di alcune persone. O forse un'altra parte del loro corpo. C -: =
Mike Nakis,

Dovrebbero davvero leggere questo: blogs.dropbox.com/developers/2015/04/…
Mike Nakis,

1
Nessun male qui. Ti sbagli di grosso. Anche l'articolo a cui ti sei collegato non menziona l'uso di codici di stato in modo così palesemente sbagliato che errori e successo sembrano uguali. Dice "Parlando di gusto, è importante ricordare che il design dell'API non riguarda strettamente le implicazioni pratiche sul software client e server. Il pubblico di un'API è lo sviluppatore che lo consumerà. Secondo il" principio del minimo stupore ", gli sviluppatori avranno più tempo a imparare e comprendere un'API se segue le stesse convenzioni delle altre API con cui hanno familiarità."
cHao,

@cHao, suppongo che la parte che dice "Dropbox attualmente utilizza circa 10 codici di stato diversi (8 errori e 2 per il successo), ma stiamo pianificando di ridurre quel numero in una versione futura dell'API" non significava nulla per tu.
Mike Nakis l'

Non intendevo quello che stai ottenendo da esso. Se sanno cosa stanno facendo, al massimo, lo ridurrebbero ad un successo e quattro codici di errore (400, 401, 404 e 500), perché ovviamente si preoccupano delle convenzioni.
cHao
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.