POST yöntemlerini HTTP'de önbelleğe almak mümkün mü?


152

Çok basit önbellek semantiği ile: parametreler aynı ise (ve URL de aynı ise, elbette), bu bir hit. Mümkün mü? Önerilen?

Yanıtlar:


93

Bölüm 9.5'teki (POST) karşılık gelen RFC 2616 , uygun başlıklar kullanırsanız POST mesajına verilen yanıtın önbelleğe alınmasına izin verir .

Yanıt uygun Önbellek Denetimi veya Süresi Sonu başlık alanlarını içermediği sürece bu yönteme verilen yanıtlar önbelleğe alınamaz. Bununla birlikte, 303 (Diğerine Bak) yanıtı, kullanıcı aracısını önbelleğe alınabilir bir kaynağı almaya yönlendirmek için kullanılabilir.

Aynı RFC'nin Bölüm 13'te (HTTP'de önbellekleme) açıkça bir önbelleğin bir POST isteğinden sonra karşılık gelen varlığı geçersiz kılması gerektiğini belirtti .

Bazı HTTP yöntemleri, bir önbelleğin bir varlığı geçersiz kılmasına neden OLMALIDIR. Bu, İstek URI'sı veya Konum veya İçerik-Konum başlıkları (varsa) tarafından belirtilen varlıktır. Bu yöntemler:

  - PUT
  - DELETE
  - POST

Bu spesifikasyonların nasıl anlamlı önbelleklemeye izin verebileceği net değil.

Bu aynı zamanda RFC 2616'yı geçersiz kılan RFC 7231'de (Bölüm 4.3.3.) Yansıtılır ve daha da netleştirilir .

POST isteklerine verilen yanıtlar yalnızca
açık tazelik bilgileri içerdiklerinde önbelleğe alınabilir (bkz. [RFC7234] Bölüm 4.2.1).
Ancak, POST önbellekleme yaygın olarak uygulanmaz. Kaynak sunucunun istemcinin POST sonucunu daha sonraki bir GET tarafından yeniden kullanılabilecek şekilde önbelleğe almasını istediği durumlarda, kaynak sunucu sonucu ve bir İçerik Konumunu içeren 200 (Tamam) yanıtı gönderebilir POST'un etkin istek URI'sı ile aynı değere sahip başlık alanı (Bölüm 3.1.4.2).

Buna göre, önbelleğe alınmış bir POST sonucu (bu yetenek sunucu tarafından belirtilirse) daha sonra aynı URI için bir GET isteğinin sonucu olarak kullanılabilir.


1
Bu bölüm, kaynak sunucu için değil, ara önbellek için (önbellek proxy sunucusu gibi) uygulanır.
David Z

2
Kaynak sunucu, HTTP ile POST isteklerini işleyen uygulama arasında bir aracıdır. Uygulama HTTP sınırının ötesindedir ve istediği her şeyi yapabilir. Önbellekleme belirli bir POST isteği için anlamlıysa, işletim sistemi disk isteklerini önbelleğe alabildiği kadar önbellekleme ücretsizdir.
Diomidis Spinellis

2
POST isteklerini önbelleğe almanın HTTP olmayacağını ifade eden Diomidis yanlıştır. Ayrıntılar için lütfen reBoot'un cevabına bakın. Yanlış cevabın en üstte görünmesi çok yararlı değil, ama demokrasi böyle işliyor. Yeniden başlatmayı kabul ediyorsanız, cevabınızı düzeltmeniz iyi olur.
Eugene Beresovsky

2
Eugene, a) POST'un önbelleğe alınmış varlığı (bölüm 13.10'a göre) geçersiz kılması gerektiği konusunda anlaşabilir miyiz; sonraki bir POST aynı yanıtı alabilir mi?
Diomidis Spinellis

3
Bu HTTPbis tarafından netleştiriliyor; özet için bkz. mnot.net/blog/2012/09/24/caching_POST .
Mark Nottingham

68

RFC 2616 Bölüm 9.5'e göre:

"POST yöntemine verilen yanıtlar önbelleğe alınamaz, yanıtın uygun Cache-Control veya Expires üstbilgisi alanlarını içermediği sürece."

Bu nedenle, EVET, POST istek yanıtını ancak uygun üstbilgilerle birlikte geldiğinde önbelleğe alabilirsiniz. Çoğu durumda yanıtı önbelleğe almak istemezsiniz. Ancak bazı durumlarda - sunucuya herhangi bir veri kaydetmiyorsanız gibi - bu tamamen uygundur.

Bununla birlikte, geçerli Firefox 3.0.10 dahil olmak üzere birçok tarayıcının, başlıklardan bağımsız olarak POST yanıtını önbelleğe almayacağını unutmayın. IE bu konuda daha akıllı davranır.

Şimdi, burada RFC 2616 S. 13.10 ile ilgili bazı karışıklıkları gidermek istiyorum. Bir URI üzerindeki POST yöntemi, burada belirtildiği gibi "önbellekleme kaynağını geçersiz kılmaz". Önbellek kontrol başlıkları daha uzun süre tazelik göstermiş olsa bile, bu URI bayatının önceden önbelleğe alınmış bir sürümünü yapar.


2
+1 reBoot, üstbilgi sorununu açıkladığınız ve 13.10 ile ilgili hatalı ifadeleri düzelttiğiniz için teşekkür ederiz. Bu yanlış cevapları şaşırtmak çok fazla oy aldı.
Eugene Beresovsky

3
"Önbelleğe almak için kaynağı geçersiz kıl" ile "URI bayatının önbelleğe alınmış bir sürümünü yapma" arasındaki fark nedir? Sunucunun bir POST yanıtını önbelleğe almasına izin verildiğini ancak istemcilerin yapamayacağını mı söylüyorsunuz?
Gili

1
"URI bayatının önbelleğe alınmış bir sürümünü oluşturma", isteklerde GETve POSTisteklerde aynı URI'yi kullandığınız durumlarda geçerlidir . İstemci ve sunucu arasında oturan bir GET /fooönbelleyseniz, yanıtı görür ve önbelleğe alırsınız. Gördüğünüz Sonraki POST /fooardından edilir gerekli dan önbelleğe yanıtı geçersiz GET /fooolsa bile POSTyanıtı hiç önbellek denetim başlıklarını içermez aynı URI çünkü böylece bir sonraki, GET /fooorijinal başlıkları önbellek hala olacağını göstermiştir bile tekrar doğrulamak zorunda kalacak canlı ( POST /fooisteği görmediyseniz )
Stephen Connolly

But in some cases - such as if you are not saving any data on the server - it's entirely appropriate.. Öyleyse ilk etapta böyle bir POST API'sının anlamı nedir?
Siddhartha

33

Genel:

Temel olarak POST, idempotent bir işlem değildir . Bu yüzden önbelleğe almak için kullanamazsınız. GET idempotent bir işlem olmalıdır, bu nedenle genellikle önbellekleme için kullanılır.

Lütfen HTTP 1.1 RFC 2616 S'nin 9.1 bölümüne bakın . 9.1 .

GET yönteminin semantiği dışında:

POST yönteminin kendisi anlamsal olarak bir kaynağa bir şeyler göndermek içindir. POST önbelleğe alınamaz çünkü bir kerede iki kez veya üç kez bir şey yaparsanız, sunucunun kaynağını her seferinde değiştirirsiniz. Her istek önemlidir ve sunucuya teslim edilmelidir.

PUT yönteminin kendisi anlamsal olarak bir kaynak koymak veya oluşturmak içindir. Bu idempotent bir işlemdir, ancak bu arada bir DELETE oluşmuş olabileceği için önbellekleme için kullanılmaz.

DELETE yönteminin kendisi anlamsal olarak bir kaynağı silmek içindir. Bu idempotent bir işlemdir, ancak önbelleğe alma için kullanılmaz çünkü bu arada bir PUT oluşmuş olabilir.

İstemci tarafı önbelleğe alma ile ilgili:

Bir web tarayıcısı, önceki bir POST işleminden yanıt almış olsa bile isteğinizi her zaman yönlendirir. Örneğin gmail ile birkaç gün arayla e-posta gönderebilirsiniz. Aynı konu ve beden olabilirler, ancak her iki e-posta da gönderilmelidir.

Proxy önbelleğe alma ile ilgili:

İletinizi sunucuya ileten proxy HTTP sunucusu, GET veya HEAD isteği dışında hiçbir şeyi önbelleğe almaz.

Sunucu önbelleğe alma ile ilgili:

Bir sunucu varsayılan olarak bir POST isteğini önbelleğini kontrol ederek otomatik olarak işlemez. Ancak elbette, uygulamanıza veya eklentinize bir POST isteği gönderilebilir ve parametreler aynı olduğunda okuduğunuz kendi önbelleğiniz olabilir.

Bir kaynağı geçersiz kılma:

HTTP 1.1'in kontrol edilmesi RFC 2616 S. 13.10 , POST yönteminin kaynağı önbellekleme için geçersiz kılması gerektiğini gösterir.


9
"Temel olarak POST idempotent bir işlem değildir. Bu nedenle önbelleğe almak için kullanamazsınız." Bu sadece yanlış ve gerçekten bir anlam ifade etmiyor, ayrıntılar için reBoot'un cevabına bakın. Ne yazık ki, henüz inemiyorum, aksi halde olurdu.
Eugene Beresovsky

1
Eugene: Ben değiştim "değil" olabilir "değil".
Brian R. Bondy

1
Teşekkürler Brian, kulağa daha iyi geliyor. "POST değil idemp. -> önbellek olamaz" ile benim sorunum oldu - ve ben yeterince net yapmadım - bir işlem önbelleğe alınamaz anlamına gelmez idempotent olmasa bile. Sanırım soru, veriyi sunan ve semantiğini bilen sunucunun bakış açısından bakıyorsunuz ya da alıcı tarafa mı bakıyorsunuz (önbellek proxy'si veya istemci mi) . İstemci / vekil pov ise, yazınızı tamamen kabul ediyorum. Sunucu pov ise, sunucu "istemci önbelleğe alabilir" diyorsa, istemci önbelleğe alabilir.
Eugene Beresovsky

1
Eugene: Bir listeye mesaj gönderiyorsanız, bir kez mi yoksa 5 kez mi çağrılması fark ederse, o çağrının sunucuya 5 kez vurmasını mı istiyorsunuz? Ve önbelleğe almak istemiyorsunuz, bu yüzden sunucuya çarpmıyor değil mi? Çünkü önemli olan yan etkiler var.
Brian R. Bondy

[devamı] Ancak, sunucu gerçekten önbellek izin süresi sona erecek sadece SADECE işlem idempotent ise üstbilgi göndermek gerekip gerekmediğini aklımda değil. Sanırım bu biraz mantıklı. [sadece cevabınızı gördüm]: Kabul ettim, bu yüzden fikrimi ortaya koyduğumu tahmin ediyorum: Sunucu sadece idempotency durumunda önbelleğe alınabilirlik sinyali vermelidir - ve bu da özellikle bir X-HTTP-Yöntem-Geçersiz Kılma ihtiyacı göz önüne alındığında bir POST olabilir. bazı durumlar.
Eugene Beresovsky

6

Bir POST yanıtını önbelleğe alırsanız, web uygulamasının yönünde olmalıdır. "Bu yönteme verilen yanıtlar, önbellek denetimi veya Expires üstbilgisi alanlarını içermiyorsa, önbelleğe alınamaz."

Bir POST sonucunun idempotent olup olmadığını bilen uygulamanın gerekli ve uygun önbellek kontrol başlıklarının eklenip eklenmeyeceğine karar verdiğini güvenle varsayabiliriz. Önbelleğe alınmaya izin veren başlıklar varsa, uygulama POST'un gerçekte bir süper GET olduğunu; POST kullanımının yalnızca idempotent işlemi gerçekleştirmek için gerekli olan gereksiz ve alakasız (URI'nin önbellek anahtarı olarak kullanılması) verilerin miktarı nedeniyle gerekli olduğunu.

Aşağıdaki GET'ler bu varsayım altında önbellekten sunulabilir.

Önbelleğe alınabilen ve alınamayan POST yanıtları arasında ayrım yapmak için gerekli ve doğru başlıkları ekleyemeyen bir uygulama, geçersiz önbellek sonuçları için hatalıdır.

Bununla birlikte, önbelleğe çarpan her POST için koşullu üstbilgiler kullanılarak doğrulama gerekir. Bu, nesnenin ömrü sona erene kadar bir POST sonucunun isteklere verilen yanıtlara yansıtılmasını önlemek için önbellek içeriğini yenilemek için gereklidir.


4

Mark Nottingham, bir POST yanıtını önbelleğe almanın mümkün olduğunu analiz etti. Önbelleklemeden yararlanmak isteyen sonraki isteklerin GET veya HEAD istekleri olması gerektiğini unutmayın. Ayrıca bkz. Http semantiği

POST'lar 100 üzerinden 99 kez belirlenen devletin temsillerini ele almazlar. Bununla birlikte, böyle bir durum söz konusudur; sunucu, bu POST yanıtının, istek URI'sıyla aynı olan bir Content-Location üstbilgisi ayarlayarak URI'sinin bir temsili olduğunu söylemek için yola çıktığında. Bu olduğunda, POST yanıtı aynı URI'ye bir GET yanıtı gibidir; önbelleğe alınabilir ve yeniden kullanılabilir - ancak yalnızca gelecekteki GET istekleri için.

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


4

Bir gönderi isteğini önbelleğe alıp alamayacağınızı merak ediyorsanız ve bu sorunun cevabını araştırmayı deneyiyorsanız, büyük olasılıkla başarılı olamayacaksınız. "Önbellek sonrası isteği" arandığında ilk sonuç bu StackOverflow sorusudur.

Yanıtlar, önbelleğe almanın nasıl çalışması, RFC'ye göre önbelleğe almanın nasıl çalıştığı, RFC'ye göre önbelleğe almanın nasıl çalışması ve pratikte önbelleğe almanın nasıl çalıştığı konusunda karışık bir karışımdır. RFC ile başlayalım, tarayıcının gerçekte nasıl çalıştığını gösterelim, ardından CDN'ler, GraphQL ve diğer endişe alanları hakkında konuşalım.

RFC 2616

RFC uyarınca, POST istekleri önbelleği geçersiz kılmalıdır:

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

Bu dil, POST isteklerinin önbelleğe alınamayacağını, ancak bu doğru olmadığını gösterir (bu durumda). Önbellek yalnızca önceden depolanmış veriler için geçersiz kılınır. RFC (görünüşe göre) açıkça evet, açıktır, POSTistekleri önbelleğe alabilirsiniz :

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.

Bu dile rağmen, Cache-Controlsonraki POSTistekleri aynı kaynağa önbelleğe almamak gerekir . POSTisteklerin sunucuya gönderilmesi gerekir:

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.

Bu nasıl bir anlam ifade ediyor? POSTİsteği önbelleğe almıyorsunuz , kaynağı önbelleğe alıyorsunuz.

POST yanıt gövdesi yalnızca aynı kaynağa GET istekleri için önbelleğe alınabilir. Set LocationveyaContent-LocationVücudun temsil ettiği kaynağı iletmek için POST yanıtında üstbilgisini . Bu nedenle, bir POST isteğini önbelleğe almanın teknik olarak geçerli tek yolu, daha sonra aynı kaynağa giden GET'ler içindir.

Doğru cevap her ikisi de:

  • "evet, RFC, sonraki GET'ler için POST isteklerini aynı kaynağa önbelleğe almanıza olanak tanır"
  • "hayır, RFC sonraki POST'ler için POST isteklerini önbelleğe almanıza izin vermez, çünkü POST idempotent değildir ve sunucuya yazılmalıdır"

RFC aynı kaynağa önbellek isteklerine izin verse de, pratikte tarayıcılar ve CDN'ler bu davranışı uygulamamakta ve POST isteklerini önbelleğe almanıza izin vermemektedir.

Kaynaklar:

Tarayıcı Davranışının Gösterilmesi

Aşağıdaki örnek JavaScript uygulaması (index.js) verildiğinde:

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!')
    })

Ve aşağıdaki örnek web sayfası (index.html) verildi:

<!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'i yükleyin ve JavaScript uygulamasını başlatın. Web sayfasını tarayıcınızda açın. Tarayıcı davranışını test etmek için birkaç farklı senaryo deneyin:

  • "GET isteğini tetikle" tıklandığında her seferinde aynı "sayım" görüntülenir (HTTP önbelleği çalışır).
  • "POST isteğini tetikle" tıklandığında her seferinde farklı bir sayı tetiklenir (POST için HTTP önbelleği çalışmaz).
  • "GET isteğini tetikle", "POST isteğini tetikle" ve "GET isteğini tetikle" tıklandığında POST isteği GET isteğinin önbelleğini geçersiz kılar.
  • "POST isteğini tetikle" ve ardından "GET isteğini tetikle" tıklandığında, tarayıcılar RFC tarafından izin verilse bile sonraki GET istekleri için POST isteklerini önbelleğe almayacaklarını gösterir.

Bu, Cache-Controlve Content-Locationyanıt başlıklarını ayarlayabilseniz bile , tarayıcı önbelleğini HTTP POST isteği yapmanın bir yolu olmadığını gösterir.

RFC'yi takip etmem gerekiyor mu?

Tarayıcı davranışı yapılandırılamaz, ancak bir tarayıcı değilseniz, RFC kurallarına bağlı olmanız gerekmez.

Uygulama kodu yazıyorsanız, POST isteklerini (sözde kod) açıkça önbelleğe almanızı engelleyen hiçbir şey yoktur:

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'lerin, proxy'lerin ve ağ geçitlerinin de RFC'yi izlemesi gerekmez. Örneğin, CDN'niz olarak Hızlı'yı kullanırsanız, POST isteklerini önbelleğe almak için Hızlıca özel VCL mantığı yazmanıza izin verir .

POST isteklerini önbelleğe almalı mıyım?

POST isteğinizin önbelleğe alınıp alınmayacağı bağlama bağlıdır.

Örneğin, temel sorgunuzun hatalı olduğu POST kullanarak Elasticsearch veya GraphQL'i sorgulayabilirsiniz. Bu durumlarda, kullanım durumuna bağlı olarak yanıtı önbelleğe almak mantıklı olabilir veya olmayabilir.

RESTful API'sinde, POST istekleri genellikle bir kaynak oluşturur ve önbelleğe alınmamalıdır. Bu aynı zamanda RFC'nin POST'un idempotent bir operasyon olmadığı anlayışıdır.

GraphQL

GraphQL kullanıyorsanız ve CDN'ler ve tarayıcılar arasında HTTP önbelleğe almanız gerekiyorsa, GET yöntemini kullanarak sorgu göndermenin POST yerine gereksinimlerinizi karşılayıp karşılamadığını düşünün . Bir uyarı olarak, farklı tarayıcılar ve CDN'ler farklı URI uzunluk sınırlarına sahip olabilir, ancak dışa dönük üretim GraphQL uygulamaları için en iyi uygulama olarak işlem güvenliği (sorgu beyaz listesi) URI'leri kısaltabilir.


3

Sitenizdeki verileri gerçekten değiştirmeyen bir şeyse, bir GET isteği olmalıdır. Bu bir form olsa bile, yine de alma isteği olarak ayarlayabilirsiniz. Diğerlerinin de belirttiği gibi, bir POST'un sonuçlarını önbelleğe alabiliyor olsanız da, anlamsal bir anlam ifade etmez çünkü tanım gereği bir POST verileri değiştiriyor.


POST isteği, yanıt sayfasını oluşturmak için kullanılan hiçbir veriyi değiştirmiyor olabilir, bu durumda yanıtı önbelleğe almanız mantıklı olabilir.
David Z

David Z: Kesinlikle bir POST veri değiştiriyorsa, yanıt bazı başarı / başarısızlık belirtileri vermelidir. Tam olarak gerekli değildir, ancak POST'un verileri değiştireceği ve yanıtın statik olduğu bir durum düşünemiyorum.
Morvael

6
Parametre verileri çok uzunsa, bir GET isteği tüm sunucularla çalışmaz, bu nedenle POST gereklidir, özellikle de kaynak kod yazarının yapılandırmadığı sunucularda çalıştırılıyorsa.
Gogowitsch

@Gogowitsch çok doğru, 414 hata koduyla karşılaşacaksınız - stackoverflow.com/a/2891598/792238
Siddhartha

-2

Firefox 27.0 ve httpfox ile, 19 Mayıs 2014'te, bunun bir satırını gördüm: 00: 03: 58.777 0.488 657 (393) POST (Önbellek) metni / html https://users.jackiszhp.info/S4UP

Açıkça, bir gönderi yönteminin yanıtı önbelleğe alınır ve https biçimindedir. Inanılmaz!


-3

POST durum bilgisi olan Ajax'ta kullanılır. POST için önbelleğe alınmış bir yanıt döndürülmesi iletişim kanalını ve mesaj almanın yan etkilerini yener. Bu Çok Çok Kötü. Ayrıca izini sürmek gerçek bir acıdır. Karşı tavsiye.

Önemsiz bir örnek, bir yan etki olarak, şu anki maaşınızı 10.000 $ ödeyen bir mesaj olacaktır. "Tamam, geçti!" geçen hafta önbelleğe alınan sayfa. Diğer, daha karmaşık gerçek dünyadaki vakalar da benzer komikliğe yol açar.


3
Gerçekten bir cevap - POST şeyler ve bazen geçerli nedenler vardır her türlü için kullanılan dilek önbellek yanıta.
Alexei Levenkov
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.