Чи можливе кешування методів POST в HTTP?


152

З дуже простою семантикою кешування: якщо параметри однакові (і, звичайно, URL-адреса однакова), то це хіт. Це можливо? Рекомендовано?

Відповіді:


93

Відповідний RFC 2616 у розділі 9.5 (POST) дозволяє кешувати відповідь на повідомлення POST, якщо ви використовуєте відповідні заголовки.

Відповіді на цей метод не підлягають кешуванню, якщо тільки відповідь не включає відповідні поля заголовка кеша або закінчується. Однак відповідь 303 (див. Інше) може бути використана для спрямування агента користувача на отримання кешованого ресурсу.

Зауважте, що той самий RFC прямо в розділі 13 (Кешування в HTTP) прямо заявляє, що кеш повинен визнати недійсним відповідний об'єкт після POST- запиту .

Деякі методи HTTP ОБОВ'ЯЗКОВО викликають кеш, щоб визнати об'єкт суттєвим. Це або сутність, на яку посилається URI-запит, або заголовки Місцеположення або Вміст-Місцеположення (якщо вони є). Такими методами є:

  - PUT
  - DELETE
  - POST

Мені не зрозуміло, як ці технічні характеристики можуть дозволити змістовне кешування.

Це також відображено та уточнено в RFC 7231 (Розділ 4.3.3.), Який застаріває RFC 2616.

Відповіді на POST-запити кешуються лише тоді, коли вони містять
явну інформацію про свіжість (див. Розділ 4.2.1 [RFC7234]).
Однак кешування POST не використовується широко. У випадках, коли сервер-джерело бажає, щоб клієнт міг кешувати результат POST таким чином, який може бути використаний більш пізнім GET, сервер-джерело МОЖЕ надіслати відповідь 200 (ОК), що містить результат та розташування вмісту поле заголовка, яке має те саме значення, що і URI ефективного запиту POST (Розділ 3.1.4.2).

Відповідно до цього, результат кешованого POST (якщо ця здатність вказаний сервером) може бути згодом використаний як результат GET-запиту на той самий URI.


1
Цей розділ поширюється на проміжний кеш (наприклад, проксі-сервер кешування), а не на початковий сервер.
Девід Z

2
Початковий сервер - це брокер між HTTP та програмою, яка обробляє POST-запити. Додаток знаходиться за межами HTTP, і він може робити все, що завгодно. Якщо кешування має сенс для конкретного POST-запиту, він може кешуватись стільки, скільки ОС може кешувати запити диска.
Діомідіс Шпінеліс

2
Diomidis, ваше твердження про те, що кешування запитів POST не буде HTTP, є помилковим. Докладнішу інформацію див. У відповіді перезавантаження. Не дуже корисно, щоб неправильна відповідь з’явилася вгорі, але так працює демократія. Якщо ви згодні з перезавантаженням, було б добре, якби ви виправили свою відповідь.
Євген Бересовський

2
Євгеній, чи можемо ми погодитись, що: a) POST повинен визнати недійсним кешовану сутність (відповідно до розділу 13.10), так що, наприклад, наступний GET повинен отримати копію та b), що відповідь POST може бути кешована (у розділі 9.5), так що, наприклад, наступний POST може отримати таку ж відповідь?
Діомідіс Шпінеліс

3
Це з'ясовується HTTPbis; дивіться mnot.net/blog/2012/09/24/caching_POST для підсумків.
Марк Ноттінгем

68

Згідно з розділом 9.5 RFC 2616:

"Відповіді на метод POST не підлягають кеш-пам'яті, ПІДТВІДЧИЙ відповідь включає відповідні поля заголовка кеша або закінчується."

Так, ТАК, ви можете кешувати відповідь на запит POST, але лише якщо він надходить із відповідними заголовками. У більшості випадків ви не хочете кешувати відповідь. Але в деяких випадках - наприклад, якщо ви не зберігаєте жодних даних на сервері - це цілком доречно.

Зауважте, проте багато браузерів, включаючи поточний Firefox 3.0.10, не кешуватимуть відповідь POST незалежно від заголовків. IE поводиться в цьому відношенні більш спритно.

Тепер я хочу прояснити деяку плутанину щодо RFC 2616 S. 13.10. Метод POST в URI не "недійсний ресурс для кешування", як дехто заявив тут. Це робить попередньо кешовану версію цього застарілого URI, навіть якщо його заголовки кеш-керування вказували на свіжість більш тривалої.


2
Перезавантажте +1, дякую за пояснення проблеми заголовків, а також виправлення помилкових тверджень щодо 13.10. Дивно, що ці неправильні відповіді отримали стільки голосів.
Євген Бересовський

3
Яка різниця між "недійсним ресурсом для кешування" та "створенням кешованої версії застарілого URI"? Ви хочете сказати, що серверу дозволено кешувати відповідь POST, але клієнти можуть не?
Гілі

1
«Роблячи кешовану версію URI несвіжих» застосовується , коли ви використовуєте один і той же URI для GETі POSTзапитів. Якщо ви кеш, який сидить між клієнтом і сервером, ви бачите GET /fooі кешуєте відповідь. Далі ви бачите, POST /fooтоді вам потрібно визнати недійсною кешовану відповідь, GET /fooнавіть якщо POSTвідповідь не містить жодних заголовків кеш-керування, оскільки вони однакові URI , тому наступне GET /fooдоведеться повторно підтвердити, навіть якщо в початкових заголовках вказано, що кеш все ще буде наживо (якщо ви не бачили POST /fooзапиту)
Стівен Конноллі,

But in some cases - such as if you are not saving any data on the server - it's entirely appropriate.. У чому сенс такого POST API в першу чергу?
Сіддхартха

33

Загалом:

В основному POST не є безвідмовною операцією . Тому ви не можете використовувати його для кешування. GET має бути безвідмовною операцією, тому її зазвичай використовують для кешування.

Дивіться розділ 9.1 HTTP 1.1 RFC 2616 S. 9.1 .

Окрім семантики методу GET:

Сам метод POST семантично призначений для публікації чогось на ресурсі. POST неможливо кешувати, оскільки якщо ви робите щось один раз проти двічі проти трьох разів, то ви щоразу змінюєте ресурс сервера. Кожен запит має значення і повинен бути доставлений на сервер.

Сам метод PUT семантично призначений для розміщення або створення ресурсу. Це ідентична операція, але вона не використовуватиметься для кешування, оскільки тим часом DELETE могла статися.

Сам метод DELETE має семантичне значення для видалення ресурсу. Це ідентична операція, але вона не буде використовуватися для кешування, оскільки PUT могла відбутися тим часом.

Щодо кешування на стороні клієнта:

Веб-браузер завжди буде пересилати ваш запит, навіть якщо він має відповідь від попередньої операції POST. Наприклад, ви можете надсилати електронні листи електронною поштою один раз за кілька днів. Вони можуть бути одним і тим же предметом і тілом, але обидва електронні листи слід надсилати.

Щодо кешування проксі:

Проксі-сервер HTTP, який пересилає ваше повідомлення на сервер, ніколи не кешуватиме нічого, крім GET або HEAD-запиту.

Щодо кешування сервера:

Сервер за замовчуванням не автоматично обробляє запит POST, перевіряючи його кеш. Але звичайно, POST-запит може бути надісланий вашій програмі або додатку, і ви можете мати власний кеш, з якого ви читали, коли параметри однакові.

Недійсний ресурс:

Перевірка HTTP 1.1 RFC 2616 S. 13.10 показує, що метод POST повинен визнати недійсним ресурс для кешування.


9
"В основному POST не є ідентичною операцією. Тому ви не можете використовувати його для кешування." Це просто неправильно, і це не має сенсу, див. Відповідь перезавантаження для деталей. На жаль, я поки що не можу сказати, що інакше було б.
Євген Бересовський

1
Євген: Я змінив "не" на "не може".
Брайан Р. Бонді

1
Дякую Брайану, це звучить краще. Моя проблема з вашим "POST не idemp. -> не може бути кешована", хоча була - і я не зробив це досить чітко - навіть якщо операція не є idempotent, це не означає, що вона не кешується. Я думаю, питання полягає в тому, чи ви дивитесь на це з точки зору сервера, який пропонує дані і знає його семантику, чи ви дивитесь на неї з боку прийому (будь то проксі-кешування тощо або клієнт) . Якщо це клієнт / проксі-сервер, я повністю згоден з вашим дописом. Якщо це сервер pov, якщо сервер каже: "клієнт може кешувати", тоді клієнт може кешувати.
Євген Бересовський

1
Євген: Якщо це має значення, чи викликається він один раз або 5 разів, наприклад, якщо ви публікуєте повідомлення в список, то ви хочете, щоб цей дзвінок 5 разів потрапив на сервер? І ви не хочете кешувати його, щоб він не потрапив на сервер правильно? Тому що є важливі побічні ефекти.
Брайан Р. Бонді

[продовження] Однак я не вирішив, чи дійсно сервер повинен надсилати ТОЛЬКО заголовок, що дозволяє кеш-пам’яті, якщо ця операція ідентична. Це має сенс, хоча, я думаю. [щойно побачив вашу відповідь]: Погодився, тому, напевно, я вирішив: Сервер повинен сигналізувати кешируемость лише у випадку ідентифікації, і це може бути також POST, особливо з огляду на необхідність переопрацювання методу X-HTTP деякі випадки.
Євген Бересовський

6

Якщо ви кешуєте відповідь POST, вона повинна відповідати вказівкам веб-програми. Це означає, що "Відповіді на цей метод не підлягають кеш-пам'яті, якщо тільки відповідь не включає відповідні поля заголовка кеша або закінчується".

Можна з упевненістю припустити, що програма, яка знає, чи є результати POST однозначними, вирішує, додавати чи ні необхідні та належні заголовки кешування керування чи ні. Якщо заголовки, які передбачають кешування, є, програма заявляє вам, що POST насправді є супер-GET; що використання POST потрібно було лише через кількість непотрібних та неактуальних (для використання URI як кеш-ключа) даних, необхідних для виконання операції ідентифікації.

Наступні GET можуть подаватися з кешу за цим припущенням.

Додаток, який не в змозі приєднати необхідні та правильні заголовки для розмежування між керованими та нехабільними відповідями POST, винен у будь-яких недійсних результатах кешування.

Однак, для кожного POST, який потрапляє в кеш, потрібна перевірка, використовуючи умовні заголовки. Це потрібно для того, щоб оновити вміст кешу, щоб уникнути того, щоб результати POST не відображалися у відповідях на запити до закінчення терміну дії об'єкта.


4

Марк Ноттінгем проаналізував, коли можливо кешувати відповідь POST. Зауважте, що наступні запити, які хочуть скористатися кешуванням, повинні бути GET або HEAD-запитами. Дивіться також http семантику

POST не розглядають уявлення про визначений стан, 99 разів із 100. Однак є один випадок, коли це відбувається; коли сервер виходить зі шляху, щоб сказати, що ця відповідь POST є відображенням його URI, встановивши заголовок Content-Location, такий самий, як URI запиту. Коли це відбувається, відповідь POST є подібно до отримання відповіді на той самий URI; його можна кешувати та використовувати повторно - але лише для майбутніх запитів GET.

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


4

Якщо вам цікаво, чи можете ви кешувати запит на публікацію, і спробуйте вивчити відповідь на це питання, ви, ймовірно, не досягнете успіху. При пошуку "запиту після кеш-повідомлення" першим результатом є це питання StackOverflow.

Відповіді - це заплутана суміш про те, як кешування має працювати, як кешування працює відповідно до RFC, як кешування має працювати відповідно до RFC та як кешування працює на практиці. Почнемо з RFC, проаналізуємо, як насправді працює браузер, а потім поговоримо про CDN, GraphQL та інші сфери, що викликають занепокоєння.

RFC 2616

За RFC, POST-запити повинні визнати недійсним кеш:

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

Ця мова говорить про те, що POST-запити не підлягають кешуванню, але це неправда (у цьому випадку). Кеш недійсний лише для раніше збережених даних. RFC (як видається) прямо уточнює, що так, ви можете кешувати POSTзапити:

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.

Незважаючи на цю мову, налаштування Cache-Controlне повинно кешувати наступні POSTзапити на один і той же ресурс. POSTзапити повинні бути надіслані на сервер:

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.

Як це має сенс? Ну, ви не кешуєте цеPOST запит, ви кешуєте ресурс.

Орган відповіді POST може кешуватися лише для наступних GET-запитів на той же ресурс. Встановіть LocationабоContent-Location заголовок у відповіді POST, щоб повідомити, який ресурс представляє тіло. Таким чином, єдиний технічно достовірний спосіб кешування запиту POST - це подальше отримання GET на той же ресурс.

Правильна відповідь:

  • "так, RFC дозволяє кешувати POST-запити для подальших GET на той же ресурс"
  • "ні, RFC не дозволяє кешувати POST-запити для наступних POST, тому що POST не є ідентичним та повинен бути записаний на сервер"

Хоча RFC дозволяє кешувати запити на один і той же ресурс, на практиці, браузери та CDN не реалізують цю поведінку і не дозволяють кешувати POST-запити.

Джерела:

Демонстрація поведінки браузера

Дано наступний приклад програми 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!')
    })

І з огляду на такий приклад веб-сторінки (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>

Встановіть NodeJS, Express та запустіть програму JavaScript. Відкрийте веб-сторінку у своєму браузері. Спробуйте кілька різних сценаріїв, щоб перевірити поведінку браузера:

  • При натисканні на "Тригер GET-запиту" кожен раз відображається однакове "підрахунок" (кешування HTTP працює).
  • Клацання "Тригер POST-запиту" щоразу запускає різний підрахунок (кешування HTTP для POST не працює).
  • При натисканні на "Тригер GET запиту", "Тригер POST-запиту" та "Тригер GET-запиту" відображається, що POST-запит недійсний кеш запиту GET.
  • Клацання "Тригер POST-запиту", а потім "Тригер GET-запиту" показує, що браузери не кешуватимуть POST-запити для наступних GET-запитів, навіть якщо це дозволено RFC.

Це показує, що, хоч ви можете встановити заголовки Cache-Controlта Content-Locationвідповіді, немає можливості зробити браузер кешувати HTTP POST-запит.

Чи потрібно слідкувати за RFC?

Поведінка веб-переглядача не налаштовується, але якщо ви не веб-переглядач, ви не обов'язково пов'язані правилами RFC.

Якщо ви пишете код програми, нічого не заважає чітко кешувати POST-запити (псевдокод):

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
}

CDN, проксі та шлюзи не обов'язково повинні дотримуватися RFC. Наприклад, якщо ви швидко використовуєте свій CDN, швидко дозволяє записати власну логіку VCL для кешування запитів POST .

Чи слід кешувати POST-запити?

Потрібно кешувати ваш запит POST чи ні, залежить від контексту.

Наприклад, ви можете запитувати Elasticsearch або GraphQL за допомогою POST, де ваш базовий запит є ідентичним. У цих випадках може або не має сенсу кешувати відповідь залежно від випадку використання.

У API RESTful, POST-запити зазвичай створюють ресурс і не повинні кешуватися. Це також розуміння RFC від POST, що це не безвідмовна операція.

GraphQL

Якщо ви використовуєте GraphQL і вимагаєте кешування HTTP у CDN та браузерах, подумайте, чи надсилає запити методом GET, а не POST . Як застереження, різні веб-переглядачі та CDN можуть мати різні межі довжини URI, але сафілістика операцій (список запитів), як найкраща практика для зовнішніх виробничих додатків GraphQL, може скоротити URI.


3

Якщо це щось, що насправді не змінює дані на вашому сайті, це повинен бути GET-запит. Навіть якщо це форма, ви все одно можете встановити її як запит на отримання. Хоча, як інші зазначають, ви можете кешувати результати POST, це не матиме смислового сенсу, оскільки POST за визначенням змінює дані.


Запит POST може не змінювати жодних даних, які використовуються для створення сторінки відповідей, і в цьому випадку це може мати сенс кешувати відповідь.
Девід Z

Девід Z: Безумовно, якщо POST змінює дані, то відповідь має вказувати на успіх / невдачу. Не потрібно точно, але я не можу придумати ситуацію, коли POST змінив би дані і відповідь була статичною.
Morvael

6
Якщо дані параметрів занадто довгі, GET-запит не працюватиме з усіма серверами, тому POST потрібен, особливо якщо джерело має працювати на серверах, які автор коду не налаштовує.
Гогович

@Gogowitsch дуже правда, ви натрапите на код помилки 414 - stackoverflow.com/a/2891598/792238
Siddhartha

-2

З Firefox 27.0 та httpfox 19 травня 2014 року я побачив один рядок цього: 00: 03: 58.777 0.488 657 (393) POST (Кеш) текст / html https://users.jackiszhp.info/S4UP

Очевидно, що відповідь методу публікації є кешованою, і вона також є в https. Неймовірно!


-3

POST використовується у великому Ajax. Повернення кешованої відповіді для POST перетворює канал зв’язку та побічні ефекти прийому повідомлення. Це дуже-дуже погано. Це також справжній біль відстежити. Настійно рекомендується проти.

Тривіальним прикладом може бути повідомлення про те, що в якості побічного ефекту виплачуєте вашу зарплату 10 000 доларів на поточному тижні. Ви НЕ хочете отримати "ОК, це пройшло!" повернення сторінки, кешоване минулого тижня. Інші, більш складні реальні випадки призводять до подібної веселості.


3
Насправді не відповідь - POST використовується для всіляких речей, а іноді є вагомі причини, щоб бажати кешувати відповідь.
Олексій Левенков
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.