Respuestas:
El RFC 2616 correspondiente en la sección 9.5 (POST) permite el almacenamiento en caché de la respuesta a un mensaje POST, si utiliza los encabezados apropiados.
Las respuestas a este método no se pueden almacenar en caché, a menos que la respuesta incluya los campos de encabezado Cache-Control o Expires apropiados. Sin embargo, la respuesta 303 (Ver otros) se puede usar para indicar al agente de usuario que recupere un recurso almacenable en caché.
Tenga en cuenta que el mismo RFC establece explícitamente en la sección 13 (Almacenamiento en caché en HTTP) que un caché debe invalidar la entidad correspondiente después de una solicitud POST .
Algunos métodos HTTP DEBEN causar que un caché invalide una entidad. Esta es la entidad a la que hace referencia el URI de solicitud o los encabezados de ubicación o ubicación de contenido (si están presentes). Estos métodos son:
- PUT - DELETE - POST
No me queda claro cómo estas especificaciones pueden permitir el almacenamiento en caché significativo.
Esto también se refleja y se aclara en RFC 7231 (Sección 4.3.3.), Que obsoleta RFC 2616.
Las respuestas a las solicitudes POST solo se pueden almacenar en caché cuando incluyen
información explícita de actualización (consulte la Sección 4.2.1 de [RFC7234]).
Sin embargo, el almacenamiento en caché POST no está ampliamente implementado. Para los casos en que un servidor de origen desea que el cliente pueda almacenar en caché el resultado de una POST de una manera que pueda ser reutilizada por un GET posterior, el servidor de origen PUEDE enviar una respuesta 200 (OK) que contenga el resultado y una ubicación de contenido campo de encabezado que tiene el mismo valor que el URI de solicitud efectiva de la POST (Sección 3.1.4.2).
De acuerdo con esto, el resultado de una POST en caché (si el servidor indica esta capacidad) puede utilizarse posteriormente como resultado de una solicitud GET para el mismo URI.
De acuerdo con RFC 2616 Sección 9.5:
"Las respuestas al método POST no se pueden almacenar en caché, A MENOS QUE la respuesta incluya los campos de encabezado Cache-Control apropiados o caduque".
Entonces, SÍ, puede almacenar en caché la respuesta de solicitud POST, pero solo si llega con los encabezados apropiados. En la mayoría de los casos, no desea almacenar en caché la respuesta. Pero en algunos casos, como si no está guardando ningún dato en el servidor, es completamente apropiado.
Tenga en cuenta que, sin embargo, muchos navegadores, incluido Firefox 3.0.10 actual, no almacenarán en caché la respuesta POST independientemente de los encabezados. IE se comporta de manera más inteligente a este respecto.
Ahora, quiero aclarar alguna confusión aquí con respecto a RFC 2616 S. 13.10. El método POST en un URI no "invalida el recurso para el almacenamiento en caché" como algunos han dicho aquí. Hace una versión previamente almacenada en caché de ese URI obsoleto, incluso si sus encabezados de control de caché indican frescura de mayor duración.
GET
y las POST
solicitudes. Si es un caché sentado entre el cliente y el servidor, verá GET /foo
y almacenará en caché la respuesta. A continuación, verá POST /foo
que debe invalidar la respuesta almacenada en caché GET /foo
incluso si la POST
respuesta no incluye ningún encabezado de control de caché porque son el mismo URI , por lo tanto, el siguiente GET /foo
tendrá que volver a validar incluso si los encabezados originales indicaron que el caché aún estaría en vivo (si no hubiera visto la POST /foo
solicitud)
But in some cases - such as if you are not saving any data on the server - it's entirely appropriate.
. ¿Cuál es el punto de tal POST API en primer lugar entonces?
En general:
Básicamente, POST no es una operación idempotente . Por lo tanto, no puede usarlo para el almacenamiento en caché. GET debe ser una operación idempotente, por lo que se usa comúnmente para el almacenamiento en caché.
Consulte la sección 9.1 de HTTP 1.1 RFC 2616 S. 9.1 .
Aparte de la semántica del método GET:
El método POST en sí mismo tiene la intención semántica de publicar algo en un recurso. POST no se puede almacenar en caché porque si hace algo una vez o dos veces o tres veces, está alterando los recursos del servidor cada vez. Cada solicitud es importante y debe entregarse al servidor.
El método PUT en sí mismo tiene la intención semántica de colocar o crear un recurso. Es una operación idempotente, pero no se utilizará para el almacenamiento en caché porque, mientras tanto, podría haberse producido una ELIMINACIÓN.
El método DELETE mismo está semánticamente destinado a eliminar un recurso. Es una operación idempotente, pero no se utilizará para el almacenamiento en caché porque, mientras tanto, podría haber ocurrido un PUT.
Con respecto al almacenamiento en caché del lado del cliente:
Un navegador web siempre reenviará su solicitud, incluso si tiene una respuesta de una operación POST anterior. Por ejemplo, puede enviar correos electrónicos con gmail con un par de días de diferencia. Pueden ser el mismo asunto y cuerpo, pero se deben enviar ambos correos electrónicos.
Con respecto al almacenamiento en caché de proxy:
Un servidor HTTP proxy que reenvía su mensaje al servidor nunca almacenará en caché nada más que una solicitud GET o HEAD.
Con respecto al almacenamiento en caché del servidor:
Un servidor por defecto no manejaría automáticamente una solicitud POST mediante la comprobación de su caché. Pero, por supuesto, se puede enviar una solicitud POST a su aplicación o complemento y puede tener su propia memoria caché de la que leer cuando los parámetros son los mismos.
Invalidación de un recurso:
La comprobación de HTTP 1.1 RFC 2616 S. 13.10 muestra que el método POST debería invalidar el recurso para el almacenamiento en caché.
Si almacena en caché una respuesta POST, debe estar en la dirección de la aplicación web. Esto es lo que se entiende por "Las respuestas a este método no se pueden almacenar en caché, a menos que la respuesta incluya los campos de encabezado Cache-Control o Expires apropiados".
Se puede suponer con seguridad que la aplicación, que sabe si los resultados de un POST son o no idempotentes, decide si se deben adjuntar o no los encabezados de control de caché necesarios y adecuados. Si hay encabezados que sugieren que el almacenamiento en caché está permitido, la aplicación le indica que la POST es, en realidad, un super-GET; que el uso de POST solo era necesario debido a la cantidad de datos innecesarios e irrelevantes (para el uso del URI como clave de caché) necesarios para realizar la operación idempotente.
Los siguientes GET se pueden servir desde el caché bajo esta suposición.
Una aplicación que no puede adjuntar los encabezados correctos y necesarios para diferenciar entre las respuestas POST almacenables en caché y no almacenables en el caché tiene la culpa de los resultados de almacenamiento en caché no válidos.
Dicho esto, cada POST que llega al caché requiere validación mediante encabezados condicionales. Esto es necesario para actualizar el contenido de la memoria caché para evitar que los resultados de una POST no se reflejen en las respuestas a las solicitudes hasta después de que expire la vida útil del objeto.
Mark Nottingham ha analizado cuándo es posible almacenar en caché la respuesta de un POST. Tenga en cuenta que las solicitudes posteriores que desean aprovechar el almacenamiento en caché deben ser solicitudes GET o HEAD. Ver también semántica http
Los POST no se ocupan de representaciones del estado identificado, 99 de cada 100 veces. Sin embargo, hay un caso en el que lo hace; cuando el servidor se sale de su camino para decir que esta respuesta POST es una representación de su URI, al establecer un encabezado Content-Location que es el mismo que el URI de solicitud. Cuando eso sucede, la respuesta POST es como una respuesta GET al mismo URI; se puede almacenar en caché y reutilizar, pero solo para futuras solicitudes GET.
Si se pregunta si puede almacenar en caché una solicitud de publicación e intentar buscar una respuesta a esa pregunta, es probable que no tenga éxito. Al buscar "solicitud de publicación en caché", el primer resultado es esta pregunta de StackOverflow.
Las respuestas son una mezcla confusa de cómo debería funcionar el almacenamiento en caché, cómo funciona el almacenamiento en caché de acuerdo con el RFC, cómo debería funcionar el almacenamiento en caché de acuerdo con el RFC y cómo funciona el almacenamiento en caché en la práctica. Comencemos con el RFC, recorramos una demostración de cómo funciona realmente el navegador, luego hablemos de CDN, GraphQL y otras áreas de interés.
Según el RFC, las solicitudes POST deben invalidar el caché:
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
Este lenguaje sugiere que las solicitudes POST no se pueden almacenar en caché, pero eso no es cierto (en este caso). El caché solo se invalida para los datos almacenados previamente. El RFC (parece) aclara explícitamente que sí, puede almacenar en caché las POST
solicitudes:
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.
A pesar de este lenguaje, la configuración de Cache-Control
no debe almacenar en caché las POST
solicitudes posteriores en el mismo recurso. POST
las solicitudes deben enviarse al servidor:
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.
¿Cómo eso tiene sentido? Bueno, no está almacenando en caché la POST
solicitud, está almacenando en caché el recurso.
El cuerpo de respuesta POST solo se puede almacenar en caché para solicitudes GET posteriores al mismo recurso. Establezca el encabezado Location
o Content-Location
en la respuesta POST para comunicar qué recurso representa el cuerpo. Por lo tanto, la única forma técnicamente válida de almacenar en caché una solicitud POST es para obtener GET posteriores en el mismo recurso.
La respuesta correcta es ambas:
Aunque el RFC permite el almacenamiento en caché de solicitudes para el mismo recurso, en la práctica, los navegadores y las CDN no implementan este comportamiento y no le permiten almacenar en caché las solicitudes POST.
Fuentes:
Dado el siguiente ejemplo de aplicación JavaScript (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!')
})
Y dado el siguiente ejemplo de página web (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>
Instale NodeJS, Express e inicie la aplicación JavaScript. Abra la página web en su navegador. Pruebe algunos escenarios diferentes para probar el comportamiento del navegador:
Esto muestra que, aunque puede configurar los encabezados Cache-Control
y la Content-Location
respuesta, no hay forma de hacer que el caché del navegador sea una solicitud HTTP POST.
El comportamiento del navegador no es configurable, pero si no eres un navegador, no estás obligado necesariamente por las reglas de la RFC.
Si está escribiendo código de aplicación, no hay nada que le impida almacenar en caché explícitamente las solicitudes POST (pseudocódigo):
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
}
Las CDN, los servidores proxy y las puertas de enlace tampoco tienen que seguir necesariamente la RFC. Por ejemplo, si usa Fastly como su CDN, Fastly le permite escribir lógica VCL personalizada para almacenar en caché las solicitudes POST .
Si su solicitud POST debe almacenarse en caché o no depende del contexto.
Por ejemplo, puede consultar Elasticsearch o GraphQL usando POST donde su consulta subyacente es idempotente. En esos casos, puede o no tener sentido almacenar en caché la respuesta según el caso de uso.
En una API RESTful, las solicitudes POST generalmente crean un recurso y no deben almacenarse en caché. Esta es también la comprensión de RFC de POST de que no es una operación idempotente.
Si está utilizando GraphQL y requiere almacenamiento en caché HTTP en CDN y navegadores, considere si el envío de consultas utilizando el método GET cumple con sus requisitos en lugar de POST . Como advertencia, los diferentes navegadores y CDN pueden tener diferentes límites de longitud de URI, pero la operación segura (lista blanca de consultas), como una mejor práctica para aplicaciones GraphQL de producción externas, puede acortar los URI.
Si es algo que en realidad no cambia los datos en su sitio, debería ser una solicitud GET. Incluso si es un formulario, puede configurarlo como una solicitud de obtención. Si bien, como señalan otros, podría almacenar en caché los resultados de una POST, no tendría sentido semántico porque una POST por definición está cambiando los datos.
Con firefox 27.0 y con httpfox, el 19 de mayo de 2014, vi una línea de esto: 00: 03: 58.777 0.488 657 (393) POST (caché) texto / html https://users.jackiszhp.info/S4UP
Claramente, la respuesta de un método de publicación se almacena en caché, y también está en https. ¡Increíble!
POST se usa en Ajax con estado. Devolver una respuesta en caché para un POST derrota el canal de comunicación y los efectos secundarios de recibir un mensaje. Esto es muy, muy malo. También es un verdadero dolor rastrear. Muy recomendable contra.
Un ejemplo trivial sería un mensaje que, como efecto secundario, paga su salario $ 10,000 la semana actual. NO DESEA obtener el "OK, ¡pasó!" página anterior que se almacenó en caché la semana pasada. Otros casos más complejos del mundo real dan como resultado una hilaridad similar.