Codice di stato HTTP per "Still Processing"


47

Sto creando un'API RESTful che supporti l'accodamento di attività di lunga durata per l'eventuale gestione.

Il flusso di lavoro tipico per questa API sarebbe:

  1. L'utente compila il modulo
  2. Il client pubblica i dati nell'API
  3. L'API restituisce 202 Accettato
  4. Il client reindirizza l'utente a un URL univoco per quella richiesta ( /results/{request_id})
  5. ~ Fine ~
  6. Il cliente visita nuovamente l'URL e vede i risultati su quella pagina.

Il mio problema è al passaggio 6. Ogni volta che un utente visita la pagina, invio una richiesta alla mia API ( GET /api/results/{request_id}). Idealmente, l'attività sarebbe stata completata ormai, e restituirei 200 OK con i risultati della loro attività.

Ma gli utenti sono invadenti e mi aspetto molti aggiornamenti troppo zelanti, quando il risultato non è ancora finito l'elaborazione.

Qual è la mia migliore opzione per un codice di stato per indicare che:

  • questa richiesta esiste,
  • non è ancora finito,
  • ma non ha fallito.

Non mi aspetto che un unico codice comunichi tutto ciò, ma mi piacerebbe qualcosa che mi permetta di passare i metadati invece che il client si aspetti il ​​contenuto.

Potrebbe avere senso restituire un 202, dal momento che qui non avrebbe altro significato: è una GETrichiesta, quindi nulla può essere "accettato". Sarebbe una scelta ragionevole?

L'ovvia alternativa a tutto ciò - che funziona, ma sconfigge uno scopo dei codici di stato - sarebbe quella di includere sempre i metadati:

200 OK

{
    status: "complete",
    data: {
        foo: "123"
    }
}

...o...

200 OK

{
    status: "pending"
}

Poi sul lato client, vorrei (sigh) switchsu response.data.statusper determinare se la richiesta è stata completata.

È questo che dovrei fare? O c'è un'alternativa migliore? Questo mi sembra proprio Web 1.0.


1
I codici 1xx non sono fatti esattamente per quello scopo?
Andy,

@Andy stavo guardando 102, ma questo è per roba WebDAV. Oltre a ciò, no ... Sono principalmente per comunicazioni in transito. Utile per passare a Web Socket e simili.
Matthew Haugen,

Di che tipo di ritardo stai parlando? 10 secondi? O 6 ore? Se i ritardi sono brevi e generalmente all'interno della stessa visita del browser, è possibile eseguire polling lunghi o socket Web anziché polling periodici.
GrandmasterB

@GrandmasterB Sono ore, potenzialmente. Non sono responsabile dell'elaborazione del lavoro stesso, quindi non ho una stima davvero buona, ma ci vorrà un po '. Altrimenti, lascerei POSTaperta la prima richiesta. Il problema principale con polling lunghi o socket Web è che l'utente potrebbe chiudere il browser e tornare. Potrei riaprirli in quel momento (ed è quello che faccio), ma sembra più pulito avere una singola API da chiamare prima di aprire quei socket, poiché è un caso limite per far sorgere quel problema.
Matthew Haugen,

Risposte:


48

HTTP 202 accettato (HTTP / 1.1)

Stai cercando lo HTTP 202 Acceptedstato. Vedi RFC 2616 :

La richiesta è stata accettata per l'elaborazione, ma l'elaborazione non è stata completata.

Elaborazione HTTP 102 (WebDAV)

RFC 2518 suggerisce di utilizzare HTTP 102 Processing:

Il codice di stato 102 (Elaborazione) è una risposta temporanea utilizzata per informare il client che il server ha accettato la richiesta completa, ma non l'ha ancora completata.

ma ha un avvertimento:

Il server DEVE inviare una risposta finale dopo il completamento della richiesta.

Non sono sicuro di come interpretare l'ultima frase. Il server dovrebbe evitare di inviare qualcosa durante l'elaborazione e rispondere solo dopo il completamento? O forza solo a terminare la risposta solo al termine dell'elaborazione? Questo potrebbe essere utile se vuoi segnalare i progressi. Invia HTTP 102 e scarica la risposta byte per byte (o riga per riga).

Ad esempio, per un processo lungo ma lineare, puoi inviare cento punti, arrossendo dopo ogni carattere. Se il lato client (come un'applicazione JavaScript) sa che dovrebbe aspettarsi esattamente 100 caratteri, può abbinarlo a una barra di avanzamento da mostrare all'utente.

Un altro esempio riguarda un processo che consiste in diversi passaggi non lineari. Dopo ogni passaggio, è possibile svuotare un messaggio di registro che verrà eventualmente visualizzato all'utente, in modo che l'utente finale possa sapere come sta andando il processo.

Problemi con lavaggio progressivo

Nota che mentre questa tecnica ha i suoi meriti, non la consiglierei . Uno dei motivi è che forza la connessione a rimanere aperta, il che potrebbe nuocere in termini di disponibilità del servizio e non si adatta bene.

Un approccio migliore è rispondere HTTP 202 Acceptede consentire all'utente di ricontattarti in un secondo momento per determinare se l'elaborazione è terminata (ad esempio chiamando ripetutamente un determinato URI come quello /process/resultche risponderebbe con HTTP 404 non trovato o HTTP 409 conflitto fino al processo termina e il risultato è pronto) oppure avvisa l'utente al termine dell'elaborazione se è possibile richiamare il client, ad esempio tramite un servizio di coda messaggi ( esempio ) o WebSocket.

Esempio pratico

Immagina un servizio web che converte i video. Il punto di ingresso è:

POST /video/convert

che prende un file video dalla richiesta HTTP e fa un po 'di magia con esso. Immaginiamo che la magia richieda un utilizzo intensivo della CPU, quindi non può essere eseguita in tempo reale durante il trasferimento della richiesta. Ciò significa che una volta trasferito il file, il server risponderà con un HTTP 202 Acceptedcontenuto JSON, il che significa "Sì, ho il tuo video e ci sto lavorando; sarà pronto da qualche parte in futuro e sarà disponibile tramite l'ID 123. "

Il client ha la possibilità di abbonarsi a una coda di messaggi per ricevere una notifica al termine dell'elaborazione. Al termine, il client può scaricare il video elaborato andando a:

GET /video/download/123

che porta a un HTTP 200.

Cosa succede se il client richiede questo URI prima di ricevere la notifica? Bene, il server risponderà HTTP 404poiché, in effetti, il video non esiste ancora. Al momento potrebbe essere preparato. Potrebbe non essere mai stato richiesto. Potrebbe esistere qualche tempo nel passato ed essere rimosso in seguito. Tutto ciò che conta è che il video risultante non è disponibile.

Ora, cosa succede se al cliente interessa non solo il video finale, ma anche i progressi (che sarebbe ancora più importante se non ci fosse un servizio di coda messaggi o un meccanismo simile)?

In questo caso, è possibile utilizzare un altro endpoint:

GET /video/status/123

che darebbe una risposta simile a questa:

HTTP 200
{
    "id": 123,
    "status": "queued",
    "priority": 2,
    "progress-percent": 0,
    "submitted-utc-time": "2016-04-19T13:59:22"
}

Eseguendo la richiesta più e più volte mostrerà l'avanzamento fino a quando non sarà:

HTTP 200
{
    "id": 123,
    "status": "done",
    "progress-percent": 100,
    "submitted-utc-time": "2016-04-19T13:59:22"
}

È fondamentale fare la differenza tra questi tre tipi di richieste:

  • POST /video/convertmette in coda un'attività. Dovrebbe essere chiamato solo una volta: richiamarlo avrebbe messo in coda un'attività aggiuntiva.
  • GET /video/download/123riguarda il risultato dell'operazione: la risorsa è il video. L'elaborazione - questo è ciò che è accaduto sotto il cofano per preparare il risultato effettivo prima della richiesta e indipendentemente dalla richiesta - è irrilevante qui. Può essere chiamato una o più volte.
  • GET /video/status/123riguarda il trattamento in . Non mette in coda nulla. Non importa del video risultante. La risorsa è l'elaborazione stessa. Può essere chiamato una o più volte.

1
Un 202 ha senso in risposta a un GET, però? Questa è sicuramente la scelta corretta per l'iniziale POST, motivo per cui la sto usando. Ma sembra semanticamente sospetto per GETdire "accettato" quando non ha accettato nulla da quella particolare richiesta.
Matthew Haugen,

2
@MainMa Come ho detto, ho POSTil lavoro da mettere in coda, quindi ho GETi risultati, potenzialmente dopo che il client ha chiuso la sessione. Un 404 è qualcosa che ho anche considerato, ma sembra sbagliato, poiché la richiesta è stata trovata, non è stata completata. Ciò indicherebbe che il lavoro in coda non è stato trovato, il che è una situazione molto diversa.
Matthew Haugen,

1
@MatthewHaugen: quando esegui la GETparte, non pensarla come una richiesta incompleta , ma come una richiesta per ottenere il risultato dell'operazione . Ad esempio, se ti dico di convertire un video e ti ci vogliono cinque minuti per farlo, la richiesta di un video convertito due minuti dopo dovrebbe risultare in HTTP 404, perché il video semplicemente non è ancora arrivato. La richiesta di avanzamento dell'operazione stessa, d'altra parte, probabilmente comporterà un HTTP 200 contenente il numero di byte convertiti, la velocità, ecc.
Arseni Mourzenko,

5
Il codice di stato HTTP per la risorsa non ancora disponibile suggerisce di restituire una risposta al conflitto 409 ("Impossibile completare la richiesta a causa di un conflitto con lo stato corrente della risorsa."), Piuttosto che una risposta 404, nel caso in cui una risorsa non non esiste perché è nel mezzo di essere generato.
Brian,

1
@Brian Il tuo commento fornirebbe una risposta ragionevole a questa domanda. Anche se risponderei quindi con "[t] il suo codice è consentito solo in situazioni in cui si prevede che l'utente possa essere in grado di risolvere il conflitto e inviare nuovamente la richiesta", il che non è strettamente vero nel mio caso, ma sembra meno sbagliato di "non trovato". Una parte di me è inclinata verso un 409 con un'intestazione Retry-After appuntata. L'unico problema è che sembra strano restituire un 409 per un GET, ma posso convivere con quella stranezza - è improbabile che venga altrimenti definito in futuro.
Matthew Haugen,

5

L'ovvia alternativa a tutto ciò - che funziona, ma sconfigge uno scopo dei codici di stato - sarebbe quella di includere sempre i metadati:

Questo è il modo corretto di procedere. Lo stato in cui si trovano le risorse rispetto al registro specifico del dominio (noto anche come logica aziendale) dipende dal tipo di contenuto della rappresentazione della risorsa.

Ci sono due concetti di differenza che si fondono qui che sono in realtà diversi. Uno è lo stato del trasferimento di stato tra client e server di una risorsa, e l'altro è lo stato della risorsa stessa in qualunque contesto il dominio aziendale comprenda i diversi stati di quella risorsa. Quest'ultimo non ha nulla a che fare con i codici di stato HTTP.

Ricorda che i codici di stato HTTP corrispondono al trasferimento di stato tra client e server della risorsa trattata, indipendentemente da qualsiasi dettaglio di quella risorsa. Quando sei GETuna risorsa, il tuo client chiede al server una rappresentazione di una risorsa nello stato attuale in cui si trova. Che potrebbe essere l'immagine di un uccello, potrebbe essere un documento di Word, potrebbe essere l'attuale temperatura esterna. Al protocollo HTTP non importa. Il codice di stato HTTP corrisponde al risultato di quella richiesta. Ha fatto il POSTdal client al server trasferire una risorsa al server, dove il server poi ha dato un URL che il cliente può visualizzare? Sì? Quindi questa è una 201 Createdrisposta.

La risorsa potrebbe essere una prenotazione di una compagnia aerea che è attualmente nello stato "da rivedere". Oppure potrebbe essere l'ordine di acquisto del prodotto che si trova nello stato "approvato". Tali stati sono specifici del dominio e non riguardano il protocollo HTTP. Il protocollo HTTP si occupa del trasferimento di risorse tra client e server.

Il punto di REST e HTTP è che i protocolli non si occupano dei dettagli delle risorse. Questo è di proposito, non si occupa dei problemi specifici del dominio in modo che possa essere utilizzato senza dover sapere nulla sui problemi specifici del dominio. Non reinterpretare cosa significano i codici di stato HTTP in ogni diverso contesto (un sistema di prenotazione di una compagnia aerea, un sistema di elaborazione di immagini, un sistema di sicurezza video ecc.).

Le cose specifiche del dominio sono per il client e il server per capire tra loro in base Content Typealla risorsa. Il protocollo HTTP è indipendente da questo.

Per quanto riguarda il modo in cui il client scopre che la risorsa richiesta è cambiata di stato, il polling è la soluzione migliore in quanto mantiene il controllo sul client e non assume una connessione ininterrotta. Soprattutto se saranno potenzialmente ore prima che lo stato cambi. Anche se hai detto all'inferno con REST, manterrai la connessione aperta, mantenendola aperta per ore e supponendo che nulla vada storto sarebbe una cattiva idea. Che cosa succede se l'utente chiude il client o la rete si spegne. Se la granularità è di ore, il client può semplicemente richiedere lo stato ogni pochi minuti fino a quando la richiesta passa da "in sospeso" a "completato".

Spero che aiuti a chiarire le cose


"Il POST dal client al server ha trasferito una risorsa al server, dove il server gli ha quindi fornito un URL che il client può visualizzare? Sì? Quindi si tratta di una risposta 201 creata." 202 Accettato è anche accettabile come risposta a ciò se il server non può agire immediatamente per elaborare la risorsa, che è ciò che sta facendo l'OP.
Andy,

1
Il fatto è che il server agisce immediatamente. Crea immediatamente la risorsa con un URL. È solo lo stato della risorsa è "In sospeso" (o qualcosa del genere). Questo è uno stato di dominio aziendale. Per quanto riguarda il protocollo HTTP, il server ha agito non appena ha creato la risorsa e ha fornito al client l'URL della risorsa. Puoi ottenere quella risorsa. La stessa richiesta POST non è in sospeso. Questo è ciò che intendo mantenendo separati i due diversi domini concettuali. Se il cliente inviava un incendio e dimenticava che la richiesta POST non era stata accettata per ore, allora 202 sarebbe applicabile.
Cormac Mulhall,

A nessuno importa se l'URL esiste ma non è possibile ottenere i dati che la risorsa rappresenta perché è ancora in fase di elaborazione. Potrebbe anche NON creare l'URL fino a quando non può essere utilizzato per ottenere il video.
Andy,

La risorsa viene creata, è solo nello stato "in sospeso". Questi sono di per sé dati pertinenti. Ad un certo punto in futuro il server potrebbe cambiare lo stato delle risorse in "completato" (o "non riuscito") ma questo è un concetto diverso rispetto all'attività specifica del dominio HTTP di "creare la risorsa". In attesa può essere uno stato perfettamente valido per una risorsa "Richiesta", e il client ovviamente vuole sapere che il server ha creato la risorsa in quello stato poiché si sposta dal chiedere al server di creare la risorsa per sapere di polling per scoprirlo se lo stato è cambiato.
Cormac Mulhall,

4

Ho trovato ragionevoli i suggerimenti di questo blog: REST e lavori di lunga durata .

Riassumere:

  1. Il server restituisce il codice "202 Accettato" con l'intestazione "Posizione" impostata su un URI affinché il client possa controllare lo stato, ad es. "/ Coda / 12345".
  2. Fino al termine dell'elaborazione, il server risponde alle richieste di stato con "200 OK" e alcuni dati di risposta che mostrano lo stato del lavoro.
  3. Al termine dell'elaborazione, il server risponde alle query di stato con "303 Visualizza altro" e "Posizione" contenente URI al risultato finale.

2

Il codice di stato HTTP per la risorsa non ancora disponibile suggerisce di restituire una risposta al conflitto 409, piuttosto che una risposta 404, nel caso in cui una risorsa non esiste perché è nel mezzo della generazione.

Dalle specifiche w3 :

10.4.10 409 Conflitto

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 di risposta DOVREBBE includere abbastanza

informazioni 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.

È più probabile che si verifichino conflitti in risposta a una richiesta PUT. Ad esempio, se si utilizzava il controllo delle versioni e l'entità in fase di PUT includeva modifiche a una risorsa in conflitto con quelle fatte da una richiesta precedente (di terze parti), il server potrebbe utilizzare la risposta 409 per indicare che non è possibile completare la richiesta . In questo caso, l'entità della risposta conterrebbe probabilmente un elenco delle differenze tra le due versioni in un formato definito dal Content-Type di risposta.

Questo è leggermente imbarazzante, poiché il codice 409 è "consentito solo nelle situazioni in cui si prevede che l'utente possa essere in grado di risolvere il conflitto e inviare nuovamente la richiesta". Suggerisco che il corpo della risposta includa un messaggio (possibilmente in un formato di risposta corrispondente al resto dell'API) come "Questa risorsa è attualmente in fase di generazione. È stata avviata alle [ORA] e si stima che si completi al [ORA]. riprovare più tardi."

Si noti che suggerirei l'approccio 409 solo se è altamente probabile che l'utente che richiede la risorsa sia anche l'utente che ha avviato la generazione di tale risorsa. Gli utenti non coinvolti nella generazione della risorsa troverebbero un errore 404 meno confuso.


Sembra un allungamento per quello che è realmente pensato per 409, che è in risposta a una put.
Andy,

@Andy: vero, ma lo è anche ogni altra alternativa. Ad esempio, 202 vuole davvero essere una risposta alla richiesta che ha avviato l' elaborazione, non alla richiesta che ha richiesto i risultati dell'elaborazione. In realtà, la risposta più conforme alle specifiche è 404, poiché la risorsa non è stata trovata (perché non esisteva ancora). Non c'è nulla che impedisce all'API di fornire i dati API pertinenti all'interno della risposta 404. Intendiamoci, le risposte 4xx / 5xx tendono ad essere fastidiose da consumare; alcune lingue genereranno un'eccezione anziché fornire semplicemente un codice di stato diverso.
Brian,

2
No, in particolare gli ultimi paragrafi della risposta di MainMa. Punti finali separati per verificare lo stato della richiesta e ottenere il video stesso. Lo stato non è la stessa risorsa del video e dovrebbe essere indirizzabile da solo.
Andy,
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.