È possibile memorizzare nella cache i metodi POST in HTTP?


152

Con una semantica di cache molto semplice: se i parametri sono gli stessi (e l'URL è lo stesso, ovviamente), allora è un successo. È possibile? Consigliato?

Risposte:


93

Il corrispondente RFC 2616 nella sezione 9.5 (POST) consente la memorizzazione nella cache della risposta a un messaggio POST, se si utilizzano le intestazioni appropriate.

Le risposte a questo metodo non sono memorizzabili nella cache, a meno che la risposta non includa i campi di intestazione Controllo cache o Scadenza appropriati. Tuttavia, la risposta 303 (Vedi altro) può essere utilizzata per indirizzare l'agente utente a recuperare una risorsa memorizzabile nella cache.

Si noti che lo stesso RFC afferma esplicitamente nella sezione 13 (Caching in HTTP) che una cache deve invalidare l'entità corrispondente dopo una richiesta POST .

Alcuni metodi HTTP DEVONO far invalidare un'entità a una cache. Questa è l'entità a cui fa riferimento l'URI di richiesta o le intestazioni Location o Content-Location (se presenti). Questi metodi sono:

  - PUT
  - DELETE
  - POST

Non mi è chiaro come queste specifiche possano consentire una memorizzazione nella cache significativa.

Ciò si riflette e chiarisce ulteriormente in RFC 7231 (Sezione 4.3.3.), Che oscura RFC 2616.

Le risposte alle richieste POST sono memorizzabili nella cache solo quando includono
informazioni esplicite sull'aggiornamento (vedere la Sezione 4.2.1 di [RFC7234]).
Tuttavia, la memorizzazione nella cache POST non è ampiamente implementata. Per i casi in cui un server di origine desidera che il client sia in grado di memorizzare nella cache il risultato di un POST in modo che possa essere riutilizzato da un GET successivo, il server di origine PUO 'inviare una risposta 200 (OK) contenente il risultato e una posizione del contenuto campo di intestazione con lo stesso valore dell'URI di richiesta effettiva del POST (Sezione 3.1.4.2).

In base a ciò, il risultato di un POST memorizzato nella cache (se questa capacità è indicata dal server) può essere successivamente utilizzato come risultato di una richiesta GET per lo stesso URI.


1
Questa sezione si applica a una cache intermedia (come un server proxy di memorizzazione nella cache), non al server di origine.
David Z,

2
Il server di origine è un broker tra HTTP e l'applicazione che gestisce le richieste POST. L'applicazione va oltre il limite HTTP e può fare quello che vuole. Se la memorizzazione nella cache ha senso per una specifica richiesta POST, è gratis memorizzare nella cache, tanto quanto il sistema operativo può memorizzare le richieste sul disco.
Diomidis Spinellis,

2
Diomidis, la tua affermazione che la memorizzazione nella cache delle richieste POST non sarebbe HTTP, è errata. Per i dettagli, consultare la risposta di reBoot. Non è molto utile far apparire la risposta sbagliata in alto, ma è così che funziona la democrazia. Se sei d'accordo con reBoot, sarebbe bello se tu avessi corretto la tua risposta.
Eugene Beresovsky,

2
Eugene, possiamo concordare sul fatto che a) il POST dovrebbe invalidare l'entità memorizzata nella cache (per la sezione 13.10), in modo che ad esempio un GET successivo debba recuperare una copia fersh eb) che la risposta del POST possa essere memorizzata nella cache (per la sezione 9.5), in modo che ad es. un POST successivo può ricevere la stessa risposta?
Diomidis Spinellis,

3
Ciò è stato chiarito da HTTPbis; vedi mnot.net/blog/2012/09/24/caching_POST per un riepilogo.
Mark Nottingham,

68

Secondo RFC 2616 Sezione 9.5:

"Le risposte al metodo POST non sono memorizzabili nella cache, A MENO CHE la risposta non includa i campi di intestazione Controllo cache o Scadenza appropriati."

Quindi, SÌ, è possibile memorizzare nella cache la risposta alla richiesta POST, ma solo se arriva con le intestazioni appropriate. Nella maggior parte dei casi non si desidera memorizzare nella cache la risposta. Ma in alcuni casi, ad esempio se non si stanno salvando dati sul server, è del tutto appropriato.

Nota, tuttavia molti browser, incluso l'attuale Firefox 3.0.10, non memorizzeranno nella cache la risposta POST indipendentemente dalle intestazioni. IE si comporta in modo più intelligente in questo senso.

Ora, voglio chiarire un po 'di confusione qui riguardo a RFC 2616 S. 13.10. Il metodo POST su un URI non "invalida la risorsa per la memorizzazione nella cache", come alcuni hanno affermato qui. Rende obsoleta una versione precedentemente memorizzata nella cache di tale URI, anche se le intestazioni del controllo cache indicano freschezza di durata maggiore.


2
+1 reBoot, grazie per aver spiegato il problema delle intestazioni e anche aver corretto le dichiarazioni errate relative al 13.10. Sorprendendo quelle risposte sbagliate ha ricevuto così tanti voti positivi.
Eugene Beresovsky,

3
Qual è la differenza tra "invalidare la risorsa per la memorizzazione nella cache" e "rendere obsoleta una versione cache dell'URI"? Stai dicendo che al server è consentito memorizzare nella cache una risposta POST ma i client potrebbero non farlo?
Gili,

1
"facendo una versione cache dell'URI stantio" si applica quando si utilizza lo stesso URI per GETe POSTrichieste. Se si trova una cache tra il client e il server, si vede GET /fooe si memorizza nella cache la risposta. Successivamente vedrai che POST /footi verrà richiesto di invalidare la risposta memorizzata nella cache GET /fooanche se la POSTrisposta non include intestazioni di controllo della cache perché sono lo stesso URI , quindi la successiva GET /foodovrà riconvalidare anche se le intestazioni originali indicavano che la cache sarebbe comunque live (se non avessi visto la POST /foorichiesta)
Stephen Connolly,

But in some cases - such as if you are not saving any data on the server - it's entirely appropriate.. A che serve allora un'API POST?
Siddhartha,

33

Complessivamente:

Fondamentalmente POST non è un'operazione idempotente . Quindi non puoi usarlo per la memorizzazione nella cache. GET dovrebbe essere un'operazione idempotente, quindi viene comunemente utilizzata per la memorizzazione nella cache.

Vedere la sezione 9.1 di HTTP 1.1 RFC 2616 S. 9.1 .

Oltre alla semantica del metodo GET:

Il metodo POST stesso è semanticamente pensato per pubblicare qualcosa su una risorsa. Il POST non può essere memorizzato nella cache perché se si fa qualcosa una volta contro due volte contro tre volte, si modifica ogni volta la risorsa del server. Ogni richiesta è importante e deve essere consegnata al server.

Lo stesso metodo PUT è semanticamente pensato per inserire o creare una risorsa. È un'operazione idempotente, ma non verrà utilizzata per la memorizzazione nella cache perché nel frattempo potrebbe essersi verificato un DELETE.

Il metodo DELETE stesso è semanticamente pensato per eliminare una risorsa. È un'operazione idempotente, ma non verrà utilizzata per la memorizzazione nella cache perché nel frattempo potrebbe essersi verificato un PUT.

Per quanto riguarda la memorizzazione nella cache lato client:

Un browser Web inoltrerà sempre la tua richiesta anche se ha una risposta da una precedente operazione POST. Ad esempio, puoi inviare e-mail con Gmail a distanza di un paio di giorni. Possono essere lo stesso soggetto e lo stesso corpo, ma entrambe le e-mail devono essere inviate.

Per quanto riguarda la memorizzazione nella cache del proxy:

Un server HTTP proxy che inoltra il tuo messaggio al server non memorizzerà mai nella cache nient'altro che una richiesta GET o HEAD.

Per quanto riguarda la memorizzazione nella cache del server:

Un server per impostazione predefinita non gestirà automaticamente una richiesta POST controllando la sua cache. Ma ovviamente una richiesta POST può essere inviata alla tua applicazione o componente aggiuntivo e puoi avere la tua cache da cui leggi quando i parametri sono gli stessi.

Invalidare una risorsa:

Il controllo di HTTP 1.1 RFC 2616 S. 13.10 mostra che il metodo POST dovrebbe invalidare la risorsa per la memorizzazione nella cache.


9
"Fondamentalmente POST non è un'operazione idempotente. Quindi non è possibile utilizzarlo per la memorizzazione nella cache." È solo sbagliato e non ha davvero senso, vedi la risposta di reBoot per i dettagli. Sfortunatamente, non posso ancora sottovalutare, altrimenti avrei.
Eugene Beresovsky,

1
Eugene: ho cambiato "non è" con "non posso".
Brian R. Bondy,

1
Grazie Brian, suona meglio. Il mio problema con il tuo "POST non idemp. -> non può essere memorizzato nella cache" era - e non l'ho chiarito abbastanza - anche se un'operazione non è idempotente che non significa che non sia memorizzabile nella cache. Immagino che la domanda sia: lo stai guardando dal punto di vista del server, chi offre i dati e ne conosce la semantica, o lo stai guardando dal lato ricevente (che si tratti di un proxy di cache ecc. O di un client) . Se è il client / proxy pov, sono totalmente d'accordo con il tuo post. Se è il server pov, se il server dice: "client può memorizzare nella cache", allora il client può memorizzare nella cache.
Eugene Beresovsky,

1
Eugene: Se fa la differenza se viene chiamato una volta o 5 volte, ad esempio se stai inviando un messaggio a un elenco, vuoi che la chiamata colpisca il server 5 volte, giusto? E non vuoi metterlo nella cache in modo che non colpisca il server giusto? Perché ci sono effetti collaterali che sono importanti.
Brian R. Bondy,

[seguito] Non ho comunque deciso se il server debba effettivamente inviare l'intestazione cache-scade SOLO se l'operazione è idempotente. In un certo senso ha senso, però, credo. [Ho appena visto la tua risposta]: Sono d'accordo, quindi immagino di aver preso una decisione: il server dovrebbe segnalare la cache solo in caso di idempotenza - e potrebbe anche essere un POST, soprattutto considerando la necessità di X-HTTP-Method-Override in alcuni casi.
Eugene Beresovsky,

6

Se si memorizza nella cache una risposta POST, deve essere nella direzione dell'applicazione Web. Questo è ciò che si intende per "Le risposte a questo metodo non sono memorizzabili, a meno che la risposta includa appropriati campi di intestazione Cache-Control o Expires".

Si può tranquillamente presumere che l'applicazione, che sa se i risultati di un POST sono o meno idempotenti, decide se collegare o meno le intestazioni di controllo della cache necessarie e appropriate. Se sono presenti intestazioni che suggeriscono la memorizzazione nella cache, l'applicazione ti sta dicendo che il POST è, in realtà, un super-GET; che l'uso di POST era richiesto solo a causa della quantità di dati non necessari e irrilevanti (per l'uso dell'URI come chiave di cache) necessari per eseguire l'operazione idempotente.

I seguenti GET possono essere forniti dalla cache in base a questo presupposto.

Un'applicazione che non riesce a collegare le intestazioni necessarie e corrette per distinguere tra risposte POST memorizzabili e non memorizzabili è responsabile di eventuali risultati di memorizzazione nella cache non validi.

Detto questo, ogni POST che colpisce la cache richiede la convalida usando le intestazioni condizionali. Ciò è necessario al fine di aggiornare il contenuto della cache per evitare che i risultati di un POST non si riflettano nelle risposte alle richieste fino a quando non scade la durata dell'oggetto.


4

Mark Nottingham ha analizzato quando è possibile memorizzare nella cache la risposta di un POST. Si noti che le richieste successive che desiderano sfruttare la memorizzazione nella cache devono essere richieste GET o HEAD. Vedi anche semantica http

I POST non trattano rappresentazioni dello stato identificato, 99 volte su 100. Tuttavia, c'è un caso in cui lo fa; quando il server fa di tutto per dire che questa risposta POST è una rappresentazione del suo URI, impostando un'intestazione Content-Location uguale all'URI della richiesta. Quando ciò accade, la risposta POST è proprio come una risposta GET allo stesso URI; può essere memorizzato nella cache e riutilizzato, ma solo per future richieste GET.

https://www.mnot.net/blog/2012/09/24/caching_POST .


4

Se ti stai chiedendo se puoi memorizzare nella cache una richiesta di post e provare a cercare una risposta a quella domanda, probabilmente non ci riuscirai. Quando si cerca "richiesta post cache" il primo risultato è questa domanda StackOverflow.

Le risposte sono una miscela confusa di come dovrebbe funzionare la memorizzazione nella cache, come la cache funziona secondo la RFC, come la cache dovrebbe funzionare secondo la RFC e come funziona la cache nella pratica. Cominciamo con RFC, illustriamo come funziona effettivamente il browser, quindi parliamo di CDN, GraphQL e altre aree di interesse.

RFC 2616

Per RFC, le richieste POST devono invalidare la cache:

13.10 Invalidation After Updates or Deletions

..

Some HTTP methods MUST cause a cache to invalidate an entity. This is
either the entity referred to by the Request-URI, or by the Location
or Content-Location headers (if present). These methods are:
  - PUT
  - DELETE
  - POST

Questo linguaggio suggerisce che le richieste POST non sono memorizzabili nella cache, ma ciò non è vero (in questo caso). La cache è invalidata solo per i dati memorizzati in precedenza. L'RFC (sembra) chiarisce esplicitamente che sì, è possibile memorizzare nella cache le POSTrichieste:

9.5 POST

..

Responses to this method are not cacheable, unless the response
includes appropriate Cache-Control or Expires header fields. However,
the 303 (See Other) response can be used to direct the user agent to
retrieve a cacheable resource.

Nonostante questa lingua, l'impostazione Cache-Controlnon deve memorizzare nella cache le POSTrichieste successive sulla stessa risorsa. POSTle richieste devono essere inviate al server:

13.11 Write-Through Mandatory

..

All methods that might be expected to cause modifications to the
origin server's resources MUST be written through to the origin
server. This currently includes all methods except for GET and HEAD.
A cache MUST NOT reply to such a request from a client before having
transmitted the request to the inbound server, and having received a
corresponding response from the inbound server. This does not prevent
a proxy cache from sending a 100 (Continue) response before the
inbound server has sent its final reply.

Che senso ha? Bene, non stai memorizzando nella cachePOST richiesta, stai memorizzando nella cache la risorsa.

Il corpo della risposta POST può essere memorizzato nella cache solo per successive richieste GET alla stessa risorsa. Impostare il LocationoContent-Location intestazione nella risposta POST per comunicare quale risorsa rappresenta il corpo. Quindi l'unico modo tecnicamente valido per memorizzare nella cache una richiesta POST, è per GET successivi alla stessa risorsa.

La risposta corretta è entrambe:

  • "sì, RFC ti consente di memorizzare nella cache le richieste POST per GET successivi nella stessa risorsa"
  • "no, RFC non consente di memorizzare nella cache le richieste POST per i POST successivi perché il POST non è idempotente e deve essere scritto sul server"

Sebbene RFC consenta la memorizzazione nella cache delle richieste alla stessa risorsa, in pratica i browser e i CDN non implementano questo comportamento e non consentono di memorizzare nella cache le richieste POST.

fonti:

Dimostrazione del comportamento del browser

Data la seguente applicazione JavaScript di esempio (index.js):

const express = require('express')
const app = express()

let count = 0

app
    .get('/asdf', (req, res) => {
        count++
        const msg = `count is ${count}`
        console.log(msg)
        res
            .set('Access-Control-Allow-Origin', '*')
            .set('Cache-Control', 'public, max-age=30')
            .send(msg)
    })
    .post('/asdf', (req, res) => {
        count++
        const msg = `count is ${count}`
        console.log(msg)
        res
            .set('Access-Control-Allow-Origin', '*')
            .set('Cache-Control', 'public, max-age=30')
            .set('Content-Location', 'http://localhost:3000/asdf')
            .set('Location', 'http://localhost:3000/asdf')
            .status(201)
            .send(msg)
    })
    .set('etag', false)
    .disable('x-powered-by')
    .listen(3000, () => {
        console.log('Example app listening on port 3000!')
    })

E data la seguente pagina Web di esempio (index.html):

<!DOCTYPE html>
<html>

<head>
    <script>
        async function getRequest() {
            const response = await fetch('http://localhost:3000/asdf')
            const text = await response.text()
            alert(text)
        }
        async function postRequest(message) {
            const response = await fetch(
                'http://localhost:3000/asdf',
                {
                    method: 'post',
                    body: { message },
                }
            )
            const text = await response.text()
            alert(text)
        }
    </script>
</head>

<body>
    <button onclick="getRequest()">Trigger GET request</button>
    <br />
    <button onclick="postRequest('trigger1')">Trigger POST request (body 1)</button>
    <br />
    <button onclick="postRequest('trigger2')">Trigger POST request (body 2)</button>
</body>

</html>

Installa NodeJS, Express e avvia l'applicazione JavaScript. Apri la pagina web nel tuo browser. Prova alcuni scenari diversi per testare il comportamento del browser:

  • Facendo clic su "Attiva richiesta GET" viene visualizzato lo stesso "conteggio" ogni volta (la memorizzazione nella cache HTTP funziona).
  • Facendo clic su "Attiva richiesta POST" si attiva ogni volta un conteggio diverso (la memorizzazione nella cache HTTP per POST non funziona).
  • Facendo clic su "Attiva richiesta GET", "Attiva richiesta POST" e "Attiva richiesta GET", la richiesta POST invalida la cache della richiesta GET.
  • Fare clic su "Attiva richiesta POST", quindi su "Attiva richiesta GET" indica che i browser non memorizzeranno nella cache le richieste POST per le richieste GET successive anche se è consentito dall'RFC.

Ciò dimostra che, sebbene sia possibile impostare le intestazioni Cache-Controle la Content-Locationrisposta, non è possibile rendere una cache HTTP del browser una richiesta POST.

Devo seguire la RFC?

Il comportamento del browser non è configurabile, ma se non si è un browser, non si è necessariamente vincolati dalle regole della RFC.

Se stai scrivendo il codice dell'applicazione, non c'è nulla che ti impedisca di memorizzare esplicitamente le richieste POST (pseudocodice):

if (cache.get('hello')) {
  return cache.get('hello')
} else {
  response = post(url = 'http://somewebsite/hello', request_body = 'world')
  cache.put('hello', response.body)
  return response.body
}

Neanche CDN, proxy e gateway devono necessariamente seguire la RFC. Ad esempio, se si utilizza Fastly come CDN, Fastly consente di scrivere logiche VCL personalizzate per memorizzare nella cache le richieste POST .

Devo memorizzare nella cache le richieste POST?

Se la tua richiesta POST deve essere memorizzata nella cache o meno dipende dal contesto.

Ad esempio, è possibile eseguire una query su Elasticsearch o GraphQL utilizzando POST in cui la query sottostante è idempotente. In questi casi, può o non ha senso memorizzare nella cache la risposta in base al caso d'uso.

In un'API RESTful, le richieste POST di solito creano una risorsa e non devono essere memorizzate nella cache. Questa è anche la comprensione da parte della RFC del POST che non si tratta di un'operazione idempotente.

GraphQL

Se stai usando GraphQL e richiedi la memorizzazione nella cache HTTP su CDN e browser, valuta se l'invio di query utilizzando il metodo GET soddisfa i tuoi requisiti anziché POST . Come avvertimento, browser e CDN diversi possono avere limiti di lunghezza URI diversi, ma la lista di sicurezza delle operazioni (whitelist di query), come best practice per le app GraphQL di produzione rivolte verso l'esterno, può abbreviare gli URI.


3

Se è qualcosa che in realtà non modifica i dati sul tuo sito, dovrebbe essere una richiesta GET. Anche se si tratta di un modulo, è comunque possibile impostarlo come richiesta get. Mentre, come altri sottolineano, è possibile memorizzare nella cache i risultati di un POST, non avrebbe senso semantico perché un POST per definizione sta cambiando i dati.


La richiesta POST potrebbe non modificare i dati utilizzati per generare la pagina di risposta, nel qual caso potrebbe avere senso memorizzare nella cache la risposta.
David Z,

David Z: Sicuramente se un POST sta modificando i dati, la risposta dovrebbe dare qualche indicazione di successo / fallimento. Non è richiesto esattamente, ma non riesco a pensare a una situazione in cui un POST cambierebbe i dati e la risposta sia statica.
Morvael,

6
Se i dati dei parametri sono troppo lunghi, una richiesta GET non funzionerà con tutti i server, pertanto è necessario il POST, soprattutto se l'origine deve essere eseguita su server che l'autore del codice non configura.
Gogowitsch,

@Gogowitsch è molto vero, ti imbatterai in un codice di errore 414 - stackoverflow.com/a/2891598/792238
Siddhartha

-2

Con firefox 27.0 e httpfox, il 19 maggio 2014, ho visto una riga di questo: 00: 03: 58.777 0.488 657 (393) POST (Cache) text / html https://users.jackiszhp.info/S4UP

Chiaramente, la risposta di un metodo post è memorizzata nella cache ed è anche in https. Incredibile!


-3

Il POST viene utilizzato in Ajax con stato. Restituire una risposta memorizzata nella cache per un POST sconfigge il canale di comunicazione e gli effetti collaterali della ricezione di un messaggio. Questo è molto, molto male. È anche un vero dolore da rintracciare. Altamente raccomandato contro.

Un esempio banale sarebbe un messaggio che, come effetto collaterale, paga il tuo stipendio $ 10.000 la settimana in corso. NON vuoi ottenere "OK, è passato!" pagina indietro che è stata memorizzata nella cache la settimana scorsa. Altri casi più complessi del mondo reale si traducono in una simile ilarità.


3
Non proprio una risposta - POST utilizzato per ogni sorta di cose e talvolta ci sono validi motivi per voler memorizzare nella cache la risposta.
Alexei Levenkov,
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.