Apakah mungkin melakukan cache metode POST dalam HTTP?

152

Dengan semantik caching yang sangat sederhana: jika parameternya sama (dan URL-nya sama, tentu saja), maka itu adalah hit. Apakah itu mungkin? Direkomendasikan?

flybywire
sumber

Jawaban:

93

RFC 2616 yang sesuai di bagian 9.5 (POST) memungkinkan penyimpanan respons terhadap pesan POST, jika Anda menggunakan header yang sesuai.

Respons terhadap metode ini tidak dapat di-cache, kecuali jika responsnya menyertakan bidang Cache-Control atau Expire header yang sesuai. Namun, respons 303 (Lihat Lainnya) dapat digunakan untuk mengarahkan agen pengguna untuk mengambil sumber daya yang dapat di-cache.

Perhatikan bahwa RFC yang sama menyatakan secara eksplisit di bagian 13 (Caching dalam HTTP) bahwa cache harus membatalkan entitas terkait setelah permintaan POST .

Beberapa metode HTTP HARUS menyebabkan cache untuk membatalkan entitas. Entah ini entitas yang dirujuk oleh Request-URI, atau oleh header Location atau Content-Location (jika ada). Metode-metode ini adalah:

  - PUT
  - DELETE
  - POST

Tidak jelas bagi saya bagaimana spesifikasi ini dapat memungkinkan caching yang bermakna.

Ini juga tercermin dan dijelaskan lebih lanjut dalam RFC 7231 (Bagian 4.3.3.), Yang menggantikan RFC 2616.

Respons terhadap permintaan POST hanya dapat di-cache ketika mereka menyertakan
informasi kesegaran eksplisit (lihat Bagian 4.2.1 dari [RFC7234]).
Namun, caching POST tidak diterapkan secara luas. Untuk kasus-kasus di mana server asal berharap klien dapat men-cache hasil POST dengan cara yang dapat digunakan kembali oleh GET yang lebih baru, server asal MUNGKIN mengirimkan respons 200 (OK) yang berisi hasil dan Lokasi-Konten bidang header yang memiliki nilai yang sama dengan URI permintaan efektif POST (Bagian 3.1.4.2).

Menurut ini, hasil POST yang di-cache (jika kemampuan ini ditunjukkan oleh server) selanjutnya dapat digunakan sebagai hasil dari permintaan GET untuk URI yang sama.

Diomidis Spinellis
sumber
1
Bagian ini berlaku untuk cache perantara (seperti server proxy caching), bukan server asal.
David Z
2
Server asal adalah perantara antara HTTP dan aplikasi yang menangani permintaan POST. Aplikasi ini di luar batas HTTP dan dapat melakukan apa pun yang diinginkan. Jika caching masuk akal untuk permintaan POST tertentu, bebas untuk melakukan cache, sebanyak OS dapat melakukan cache permintaan.
Diomidis Spinellis
2
Diomidis, pernyataan Anda bahwa cache permintaan POST tidak akan HTTP, salah. Silakan lihat jawaban reBoot untuk detailnya. Tidak terlalu membantu jika jawaban yang salah muncul di puncak, tetapi itulah cara kerja demokrasi. Jika Anda setuju dengan reBoot, alangkah baiknya jika Anda mengoreksi jawaban Anda.
Eugene Beresovsky
2
Eugene, dapatkah kita sepakat bahwa a) POST harus membatalkan entitas yang di-cache (per bagian 13.10), sehingga misalnya GET berikutnya harus mengambil salinan fersh dan b) bahwa respons POST dapat di-cache (per bagian 9.5), sehingga misalnya POST berikutnya dapat menerima respons yang sama?
Diomidis Spinellis
3
Ini sedang diklarifikasi oleh HTTPbis; lihat mnot.net/blog/2012/09/24/caching_POST untuk ringkasan.
Mark Nottingham
68

Menurut RFC 2616 Bagian 9.5:

"Respons terhadap metode POST tidak dapat di-cache, KECUALI responsnya termasuk bidang Cache-Control atau Expires header yang sesuai."

Jadi, YA, Anda bisa menyimpan respons permintaan POST tetapi hanya jika ada header yang sesuai. Dalam kebanyakan kasus, Anda tidak ingin menyimpan tanggapan. Tetapi dalam beberapa kasus - seperti jika Anda tidak menyimpan data apa pun di server - itu sepenuhnya tepat.

Catatan, namun banyak peramban, termasuk Firefox 3.0.10 saat ini, tidak akan menyimpan respons POST terlepas dari header. IE berperilaku lebih pintar dalam hal ini.

Sekarang, saya ingin menjernihkan kebingungan di sini mengenai RFC 2616 S. 13.10. Metode POST pada URI tidak "membatalkan sumber daya untuk caching" seperti yang telah dinyatakan di sini. Itu membuat versi URI yang sebelumnya di-cache basi, bahkan jika header kontrol cache menunjukkan kesegaran durasi yang lebih lama.


sumber
2
+1 reBoot, terima kasih telah menjelaskan masalah tajuk dan juga memperbaiki pernyataan yang salah tentang 13.10. Mengejutkan jawaban yang salah itu menerima begitu banyak suara.
Eugene Beresovsky
3
Apa perbedaan antara "membatalkan sumber daya untuk caching" dan "membuat versi cache URI yang di-cache"? Apakah Anda mengatakan bahwa server diizinkan untuk menembolok respons POST tetapi klien mungkin tidak?
Gili
1
"membuat versi cache URI yang di-cache" berlaku di mana Anda menggunakan GETdan POSTpermintaan URI yang sama . Jika Anda adalah cache yang berada di antara klien dan server, Anda melihat GET /foodan Anda menyimpan respons. Selanjutnya Anda melihat POST /foomaka Anda diharuskan untuk membatalkan respons yang di-cache dari GET /foobahkan jika POSTrespons tersebut tidak menyertakan header kontrol cache apa pun karena mereka adalah URI yang sama , sehingga berikutnya GET /fooharus memvalidasi ulang bahkan jika header asli mengindikasikan cache masih akan menjadi live (jika Anda belum melihat POST /foopermintaan)
Stephen Connolly
But in some cases - such as if you are not saving any data on the server - it's entirely appropriate.. Apa gunanya POST API seperti itu?
Siddhartha
33

Secara keseluruhan:

Pada dasarnya POST bukan operasi idempoten . Jadi Anda tidak bisa menggunakannya untuk caching. GET harus merupakan operasi idempoten, jadi ini biasa digunakan untuk caching.

Silakan lihat bagian 9.1 dari HTTP 1.1 RFC 2616 S. 9.1 .

Selain dari semantik metode GET:

Metode POST itu sendiri secara semantik dimaksudkan untuk memposting sesuatu ke sumber. POST tidak dapat di-cache karena jika Anda melakukan sesuatu sekali vs dua kali vs tiga kali, maka Anda mengubah sumber daya server setiap kali. Setiap permintaan penting dan harus dikirimkan ke server.

Metode PUT itu sendiri secara semantik dimaksudkan untuk menempatkan atau membuat sumber daya. Ini adalah operasi idempoten, tetapi itu tidak akan digunakan untuk caching karena DELETE bisa saja terjadi sementara itu.

Metode DELETE itu sendiri semantik dimaksudkan untuk menghapus sumber daya. Ini adalah operasi idempoten, tetapi itu tidak akan digunakan untuk caching karena PUT bisa saja terjadi sementara itu.

Mengenai caching sisi klien:

Peramban web akan selalu meneruskan permintaan Anda, bahkan jika ada respons dari operasi POST sebelumnya. Misalnya Anda dapat mengirim email dengan gmail yang terpisah beberapa hari. Mereka mungkin subjek dan badan yang sama, tetapi kedua email harus dikirim.

Mengenai caching proxy:

Server HTTP proksi yang meneruskan pesan Anda ke server tidak akan pernah menembolok apa pun selain permintaan GET atau HEAD.

Mengenai caching server:

Server secara default tidak akan secara otomatis menangani permintaan POST melalui pengecekan cache-nya. Tetapi tentu saja permintaan POST dapat dikirim ke aplikasi atau add-in Anda dan Anda dapat memiliki cache sendiri yang Anda baca dari saat parameternya sama.

Memvalidasi sumber daya:

Memeriksa HTTP 1.1 RFC 2616 S. 13.10 menunjukkan bahwa metode POST harus membatalkan sumber daya untuk caching.

Brian R. Bondy
sumber
9
"Pada dasarnya POST bukan operasi idempoten. Jadi kamu tidak bisa menggunakannya untuk caching." Itu hanya salah, dan itu tidak masuk akal, lihat jawaban reBoot untuk detailnya. Sayangnya, saya belum bisa downvote, kalau tidak saya akan lakukan.
Eugene Beresovsky
1
Eugene: Saya mengubah "tidak" menjadi "mungkin tidak".
Brian R. Bondy
1
Terima kasih Brian, kedengarannya lebih baik. Masalah saya dengan "POST not idemp. -> Anda tidak dapat di-cache" meskipun adalah - dan saya tidak membuatnya cukup jelas - meskipun sebuah operasi bukan idempoten yang tidak berarti tidak dapat di-cache. Saya kira pertanyaannya adalah apakah Anda melihatnya dari sudut pandang server, yang menawarkan data dan mengetahui semantiknya, atau apakah Anda melihatnya dari sisi penerima (baik itu proxy caching dll atau klien) . Jika klien / proxy pov, saya setuju sepenuhnya dengan posting Anda. Jika itu server pov, jika server mengatakan: "client can cache", maka client dapat cache.
Eugene Beresovsky
1
Eugene: Jika ada bedanya apakah itu dipanggil sekali atau 5 kali, seperti jika Anda memposting pesan ke daftar, maka Anda ingin panggilan itu mengenai server 5 kali benar? Dan Anda tidak ingin men-cache itu sehingga tidak mengenai server kan? Karena ada efek samping yang penting.
Brian R. Bondy
[cont'd] Namun saya belum memutuskan apakah server memang harus mengirim header yang memungkinkan cache-cache HANYA jika operasi idempoten. Tapi itu masuk akal, kurasa. [baru saja melihat respons Anda]: Setuju, jadi saya pikir saya memutuskan: Server seharusnya hanya memberi sinyal cacheability jika idempotensi - dan itu bisa menjadi POST juga, terutama mengingat perlunya X-HTTP-Method-Override di beberapa kasus.
Eugene Beresovsky
6

Jika Anda melakukan cache respons POST, itu harus sesuai dengan arah aplikasi web. Inilah yang dimaksud dengan "Respons terhadap metode ini tidak dapat di-cachable, kecuali jika responsnya menyertakan bidang Cache-Control atau Expires header yang sesuai."

Orang dapat dengan aman berasumsi bahwa aplikasi, yang mengetahui apakah hasil POST idempoten atau tidak, memutuskan apakah akan memasang header kontrol cache yang diperlukan dan tepat. Jika tajuk yang menyarankan caching diperbolehkan ada, aplikasi memberi tahu Anda bahwa POST, pada kenyataannya, adalah GET-super; bahwa penggunaan POST hanya diperlukan karena jumlah data yang tidak perlu dan tidak relevan (untuk penggunaan URI sebagai kunci cache) yang diperlukan untuk melakukan operasi idempoten.

Mengikuti GET dapat dilayani dari cache dengan asumsi ini.

Aplikasi yang gagal melampirkan tajuk yang diperlukan dan benar untuk membedakan antara respons POST yang tidak dapat ditutup dan tidak bisa dikirim adalah kesalahan untuk setiap hasil caching yang tidak valid.

Yang mengatakan, setiap POST yang mengenai cache memerlukan validasi menggunakan header kondisional. Ini diperlukan untuk me-refresh konten cache untuk menghindari agar hasil POST tidak tercermin dalam respons terhadap permintaan hingga setelah masa hidup objek berakhir.

John
sumber
4

Mark Nottingham telah menganalisis kapan layak untuk menembaki respons POST. Perhatikan bahwa permintaan berikutnya yang ingin memanfaatkan caching haruslah GET atau permintaan HEAD. Lihat juga semantik http

POST tidak berurusan dengan representasi negara yang teridentifikasi, 99 kali dari 100. Namun, ada satu kasus di mana ia melakukannya; ketika server keluar dari cara untuk mengatakan bahwa respons POST ini adalah representasi dari URI-nya, dengan mengatur header Konten-Lokasi yang sama dengan URI permintaan. Ketika itu terjadi, respons POST sama seperti respons GET untuk URI yang sama; itu bisa di-cache dan digunakan kembali - tetapi hanya untuk permintaan GET di masa depan.

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

dschulten
sumber
4

Jika Anda bertanya-tanya apakah Anda dapat men-cache permintaan posting, dan mencoba meneliti jawaban untuk pertanyaan itu, Anda kemungkinan tidak akan berhasil. Saat mencari "permintaan posting cache" hasil pertama adalah pertanyaan StackOverflow ini.

Jawabannya adalah campuran bingung tentang bagaimana caching harus bekerja, bagaimana caching bekerja sesuai dengan RFC, bagaimana caching harus bekerja sesuai dengan RFC, dan bagaimana caching bekerja dalam praktik. Mari kita mulai dengan RFC, telusuri demonstrasi tentang bagaimana sebenarnya browser berfungsi, kemudian bicarakan CDN, GraphQL, dan bidang lain yang menjadi perhatian.

RFC 2616

Per RFC, permintaan POST harus membatalkan 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

Bahasa ini menunjukkan bahwa permintaan POST tidak dapat di-cache, tetapi itu tidak benar (dalam hal ini). Cache hanya tidak valid untuk data yang disimpan sebelumnya. RFC (tampaknya) secara eksplisit menjelaskan bahwa ya, Anda dapat men-cache POSTpermintaan:

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.

Terlepas dari bahasa ini, pengaturan Cache-Controltidak boleh menembolok POSTpermintaan berikutnya ke sumber yang sama. POSTpermintaan harus dikirim ke 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.

Bagaimana itu masuk akal? Nah, Anda tidak menembaki POSTpermintaan, Anda men-cache sumber daya.

Badan respons POST hanya bisa di-cache untuk permintaan GET berikutnya ke sumber yang sama. Setel tajuk Locationatau Content-Locationdi respons POST untuk mengomunikasikan sumber daya yang diwakili oleh badan. Jadi satu-satunya cara yang valid secara teknis untuk men-cache permintaan POST, adalah untuk mendapatkan GET berikutnya ke sumber daya yang sama.

Jawaban yang benar adalah keduanya:

  • "Ya, RFC memungkinkan Anda untuk me-cache permintaan POST untuk GET berikutnya ke sumber daya yang sama"
  • "tidak, RFC tidak memungkinkan Anda untuk me-cache permintaan POST untuk POST berikutnya karena POST tidak idempoten dan harus ditulis melalui server"

Meskipun RFC memungkinkan untuk caching permintaan ke sumber yang sama, dalam praktiknya, browser dan CDN tidak menerapkan perilaku ini, dan tidak memungkinkan Anda untuk me-cache permintaan POST.

Sumber:

Demonstrasi Perilaku Peramban

Diberikan contoh aplikasi JavaScript berikut (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!')
    })

Dan diberikan contoh halaman web berikut (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>

Instal NodeJS, Express, dan mulai aplikasi JavaScript. Buka halaman web di browser Anda. Coba beberapa skenario berbeda untuk menguji perilaku browser:

  • Mengklik "Trigger GET request" menampilkan "hitungan" yang sama setiap kali (caching HTTP berfungsi).
  • Mengklik "Trigger POST request" memicu jumlah yang berbeda setiap kali (caching HTTP untuk POST tidak berfungsi).
  • Mengklik "Trigger GET request", "Trigger permintaan POST", dan "Trigger GET request" menunjukkan permintaan POST membatalkan cache permintaan GET.
  • Mengklik "Trigger POST request" lalu "Trigger GET request" menunjukkan bahwa browser tidak akan men-cache permintaan POST untuk permintaan GET berikutnya meskipun itu diizinkan oleh RFC.

Ini menunjukkan bahwa, meskipun Anda dapat mengatur Cache-Controldan Content-Locationmenanggapi header, tidak ada cara untuk membuat cache browser permintaan HTTP POST.

Apakah saya harus mengikuti RFC?

Perilaku browser tidak dapat dikonfigurasi, tetapi jika Anda bukan browser, Anda tidak harus terikat oleh aturan RFC.

Jika Anda menulis kode aplikasi, tidak ada yang menghentikan Anda untuk secara eksplisit menyimpan permintaan POST (pseudocode):

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, proksi, dan gateway tidak harus mengikuti RFC juga. Misalnya, jika Anda menggunakan Fastly sebagai CDN Anda, Fastly memungkinkan Anda untuk menulis logika VCL khusus untuk men-cache permintaan POST .

Haruskah saya menyimpan permintaan POST?

Apakah permintaan POST Anda harus di-cache atau tidak tergantung pada konteksnya.

Misalnya, Anda dapat meminta Elasticsearch atau GraphQL menggunakan POST di mana permintaan dasar Anda idempoten. Dalam kasus tersebut, mungkin perlu atau tidak masuk akal untuk men-cache respon tergantung pada use case.

Di API RESTful, permintaan POST biasanya membuat sumber daya dan tidak boleh di-cache. Ini juga merupakan pemahaman RFC tentang POST bahwa itu bukan operasi idempoten.

GraphQL

Jika Anda menggunakan GraphQL dan membutuhkan caching HTTP di seluruh CDN dan browser, pertimbangkan apakah mengirim kueri menggunakan metode GET memenuhi persyaratan Anda alih-alih POST . Sebagai peringatan, browser dan CDN yang berbeda mungkin memiliki batas panjang URI yang berbeda, tetapi operasi daftar aman (daftar putih kueri), sebagai praktik terbaik untuk aplikasi GraphQL yang menghadap ke luar, dapat mempersingkat URI.

timrs2998
sumber
3

Jika itu sesuatu yang tidak benar-benar mengubah data di situs Anda, itu haruslah permintaan GET. Bahkan jika itu adalah formulir, Anda masih dapat mengaturnya sebagai permintaan dapatkan. Sementara, seperti yang ditunjukkan orang lain, Anda bisa men-cache hasil POST, itu tidak masuk akal semantik karena POST menurut definisi mengubah data.

Kibbee
sumber
Permintaan POST mungkin tidak mengubah data apa pun yang digunakan untuk menghasilkan halaman respons, dalam hal ini masuk akal untuk men-cache respons.
David Z
David Z: Tentunya jika POST mengubah data, maka responsnya harus memberikan indikasi keberhasilan / kegagalan. Tidak diperlukan dengan tepat, tapi saya tidak bisa memikirkan situasi di mana POST akan mengubah data dan responsnya statis.
Morvael
6
Jika data parameter terlalu panjang, permintaan GET tidak akan berfungsi dengan semua server, maka POST diperlukan, terutama jika sumber harus berjalan di server yang tidak dikonfigurasikan oleh pembuat kode.
Gogowitsch
@ Gogowitsch sangat benar, Anda akan mengalami kode kesalahan 414 - stackoverflow.com/a/2891598/792238
Siddhartha
-2

Dengan firefox 27.0 & dengan httpfox, pada 19 Mei 2014, saya melihat satu baris dari ini: 00: 03: 58.777 0.488 657 (393) teks POST (Cache) / html https://users.jackiszhp.info/S4UP

Jelas, respons metode posting di-cache, dan juga di https. Luar biasa!

pengguna1462586
sumber
-3

POST digunakan di stateful Ajax. Mengembalikan respons yang di-cache untuk POST mengalahkan saluran komunikasi dan efek samping dari menerima pesan. Ini Sangat Sangat Buruk. Ini juga menyulitkan untuk dilacak. Sangat direkomendasikan.

Contoh sepele adalah pesan bahwa, sebagai efek samping, membayar gaji Anda $ 10.000 minggu ini. Anda TIDAK ingin mendapatkan "OK, sudah lewat!" halaman belakang yang di-cache minggu lalu. Kasus dunia nyata lainnya yang lebih kompleks menghasilkan keriuhan yang serupa.

DragonLord
sumber
3
Tidak benar-benar jawaban - POST digunakan untuk segala macam hal dan kadang-kadang ada alasan yang sah untuk menginginkan tanggapan cache
Alexei Levenkov