SEÇENEK isteği neden gönderilir ve devre dışı bırakabilir miyim?


415

Bir web API'sı oluşturuyorum. Chrome'u POST yapmak için kullandığımda, API'ma GET'i her kullandığımda, her zaman gerçek istekten önce gönderilen ve oldukça can sıkıcı bir OPTIONS isteği olduğunu buldum. Şu anda herhangi bir OPTIONS isteklerini yoksaymak için sunucu olsun. Şimdi sorularım, sunucunun yükünü iki katına çıkarmak için OPTIONS isteği göndermenin iyi yanı nedir? Tarayıcının OPTIONS istekleri göndermesini tamamen durdurmanın bir yolu var mı?

Yanıtlar:


376

edit 2018-09-13 : bu uçuş öncesi isteği ve bu yanıtın sonunda nasıl önleneceği hakkında bazı kesinlikler ekledi.

OPTIONSistekleri dediğimiz vardır pre-flightistekleri Cross-origin resource sharing (CORS).

Belirli durumlarda farklı kökenlerden talepte bulunurken gereklidir.

Bu uçuş öncesi isteği, bazı tarayıcılar tarafından, yapılan isteğin sunucu tarafından güvenildiğinden emin olmak için bir güvenlik önlemi olarak yapılır. Yani sunucu, istek üzerine gönderilen yöntemin, kaynağın ve başlıkların üzerinde işlem yapmanın güvenli olduğunu anlar.

Çapraz kökenli istekler yapmaya çalıştığınızda sunucunuz bu istekleri dikkate almamalı ve işlememelidir.

İyi bir kaynak burada bulunabilir http://enable-cors.org/

Bunları rahat ettirmenin bir yolu, OPTIONSyöntemle herhangi bir yol için sunucunun bu üstbilgiyle bir yanıt göndermesini sağlamaktır

Access-Control-Allow-Origin: *

Bu tarayıcıya sunucunun herhangi bir kaynaktan gelen istekleri cevaplamak istediğini söyleyecektir.

Sunucunuza CORS desteği ekleme hakkında daha fazla bilgi için aşağıdaki akış şemasına bakın

http://www.html5rocks.com/static/images/cors_server_flowchart.png

CORS Akış Şeması


düzenleme 2018-09-13

MDN belgelerindeOPTIONS açıklandığı gibi CORS talebi yalnızca bazı durumlarda tetiklenir :

Bazı istekler bir CORS ön kontrolünü tetiklemez. Bu makalede bunlara “basit istekler” denir, ancak Getirme özelliği (CORS'yi tanımlayan) bu terimi kullanmaz. CORS ön kontrolünü ("basit istek" adı verilen) tetiklemeyen bir istek, aşağıdaki koşulların tümünü karşılar:

İzin verilen yöntemler şunlardır:

  • ALMAK
  • BAŞ
  • İLETİ

Kullanıcı aracısı tarafından otomatik olarak ayarlanan başlıklar dışında (örneğin, Bağlantı, Kullanıcı Aracısı veya Getirme özelliğinde “yasak başlık adı” olarak tanımlanan adlara sahip diğer başlıklar), El ile ayarlananlar, Fetch spesifikasyonunun “CORS destekli istek başlığı” olarak tanımladığı, aşağıdakilerdir:

  • Kabul etmek
  • Accept-Language
  • Content-Language
  • İçerik Türü (ancak aşağıdaki ek gereksinimleri not edin)
  • DPR
  • downlink
  • Veri kaydet
  • Görünüm genişlikte
  • Genişlik

İçerik Türü başlığı için izin verilen değerler şunlardır:

  • Uygulama / x-www-form-urlencoded
  • Çok parçalı / form-
  • metin / düz

İstekte kullanılan XMLHttpRequestUpload nesnesine hiçbir olay dinleyicisi kaydedilmez; bunlara XMLHttpRequest.upload özelliği kullanılarak erişilir.

İstekte ReadableStream nesnesi kullanılmıyor.


8
Ancak bu Chrome bayrağını tüm genel kullanıcılara ayarlamak gerçekçi değildir.
Qian Chen

37
Çapraz menşe istekleri yapılırken ön kontrol taleplerinin gerekli olduğunu söylemek yanlıştır. Ön kontrol istekleri, yalnızca özel üstbilgiler ayarlıyor veya get, head ve post dışında istekler yapıyor gibi belirli durumlarda gereklidir.
Robin Clowers

4
JQuery kullanarak bir CORS isteği yaparken, JavaScript kitaplığı özellikle geliştiricilere uyarı kelimesi ile birlikte özel üstbilgiyi ayarlamayı önler: Alanlar arası talepler için, bir ön kontrol için koşulların bir bilmecenin benzer olduğunu görmek, sadece emin olmak için asla ayarlama.
Radarın Altında

3
Nasıl curlçalışır bir api için yapmak , ama krom çalışırken ben hata alıyorum?
SuperUberDuper

5
@SuperUberDuper çünkü CORS ve ön kontrol istekleri tarayıcıyla ilgili bir konudur. Simülasyon Originisteğinize belirli bir ana bilgisayardan (ör. Web siteniz.com) geliyormuş gibi benzetmek için bir başlık ekleyerek CORS'yi simüle edebilirsiniz. Ön kontrol isteklerini, bir isteğin HTTP yöntemini OPTIONSve Access-Control-*Üstbilgiler'i ayarlayarak da taklit edebilirsiniz
Leo Correa

234

Bu sorunu yaşadım, aşağıda bu konuya varmam ve çözümüm.

Göre CORS stratejisi Sadece biraz daha ihtiyacı düşünürse SEÇENEKLER talep göndermeyi durdurmak için tarayıcı zorlayamaz (yüksek oranda bu konuda okumak öneririz).

Bu sorunu çözmenin iki yolu vardır:

  1. İsteğinizin "basit bir istek" olduğundan emin olun
  2. Access-Control-Max-AgeOPTIONS isteği için ayarla

Basit istek

Siteler arası basit bir istek, aşağıdaki koşulların tümünü karşılayan istektir:

İzin verilen yöntemler şunlardır:

  • ALMAK
  • BAŞ
  • İLETİ

Kullanıcı aracısı tarafından otomatik olarak ayarlanan başlıklar dışında (örn. Bağlantı, Kullanıcı Aracısı, vb.), Manuel olarak ayarlanmasına izin verilen tek başlıklar şunlardır:

  • Kabul etmek
  • Accept-Language
  • Content-Language
  • İçerik türü

İçerik Türü başlığı için izin verilen değerler şunlardır:

  • Uygulama / x-www-form-urlencoded
  • Çok parçalı / form-
  • metin / düz

Basit bir istek, uçuş öncesi SEÇENEKLER isteğine neden olmaz.

SEÇENEKLER denetimi için önbellek ayarlama

Access-Control-Max-AgeSEÇENEKLER isteği için a'yı ayarlayabilirsiniz , böylece süre dolana kadar izni tekrar kontrol etmez.

Access-Control-Max-Age, ön kontrol isteğine verilen yanıtın başka bir ön kontrol isteği gönderilmeden ne kadar süreyle önbelleğe alınabileceği için saniye cinsinden değeri verir.

Sınırlama Bildirildi

  • Chrome için Access-Control-Max-Age, krom kaynak koduna600 göre maksimum saniye süresi 10 dakikadır.
  • Access-Control-Max-Ageher seferinde yalnızca bir kaynak için çalışır, örneğin, GETaynı URL yoluna sahip istekler, ancak farklı sorgular farklı kaynaklar olarak değerlendirilir. Dolayısıyla ikinci kaynağa olan talep yine de ön kontrol talebini tetikleyecektir.

3
Evet ... bu kabul edilen cevap olmalı ve soru ile en alakalı olmalı ..!
Rajesh Mbm

7
Bahsettiğiniz için teşekkürler Access-Control-Max-Age. Buradaki anahtar. Aşırı ön kontrol isteklerinden kaçınmanıza yardımcı olur.
Idris Mokhtarzada

Ben istek almak için axios kullanıyorum. Aksios talebinde Erişim-Kontrol-Maks-Yaş'ı nereden ayarlayabilirim?
Mohit Shah

+1 Access-Control-Max-Age üstbilgisi buradaki anahtardır. Bu kabul edilen cevap olmalı! Başlığa 86400 saniye (24 saat) ayarladım ve ön istek isteği kayboldu!
revobtz

1
@ErkekTerimleri application/jsonSadece isteğinizi "basit" yapmadığından (ve dolayısıyla CORS'i tetiklediğinden) kaçının . Tarayıcı işini yapıyor. Sunucunuzu başlık gibi bir şey döndürecek şekilde ayarlayın Access-Control-Max-Age: 86400; tarayıcı 24 saat boyunca bir SEÇENEK isteği göndermeyecektir.
colm.anseo

139

Lütfen bu cevabı önceden bildirilmiş SEÇENEKLER talebinin gerçek ihtiyacına bakın: CORS - Ön kontrol taleplerini getirmenin arkasındaki motivasyon nedir?

OPTIONS isteğini devre dışı bırakmak için ajax isteği için aşağıdaki koşulların karşılanması gerekir:

  1. İstek, 'application / xml' veya 'application / json' vb. Gibi özel HTTP üstbilgileri ayarlamıyor
  2. İstek yöntemi GET, HEAD veya POST olmalıdır. POST, içerik türü biri olmalıdır application/x-www-form-urlencoded, multipart/form-dataya datext/plain

Referans: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS


14
"Özel HTTP üstbilgileri" için +1! Benim durumumda, uçuş öncesi talebin tetiklenmesine neden oluyordu. İstek gövdesi ve OPTIONS isteklerinin gönderilmesi durduğundan, başlıklarda gönderdiğim her şeyi gönderme isteğini yeniden düzenledim.
Andre

21
application/xmlveya application/json"Özel HTTP üstbilgileri" değildir. Başlığın kendisi olur Content-Typeve bu başlığa "özel" demek yanıltıcı olur.
Leo Correa

1
Özel HTTP Üstbilgileri kaldırıldı ve bu bir cazibe gibi çalıştı!
Tim D

47

Hata ayıklama konsolu açık ve Disable Cacheseçenek açık olduğunda, ön kontrol istekleri her zaman gönderilir (yani her istekden önce). önbelleği devre dışı bırakmazsanız, bir uçuş öncesi isteği yalnızca bir kez gönderilir (sunucu başına)


3
oh ne düşünüyorum. saatlerce hata ayıklama bu benim çözüm oldu. hata ayıklama konsolu nedeniyle önbellek devre dışı.
mauris

1
Hata ayıklama konsolu kapatılsa bile ön kontrol istekleri gönderilir
Luca Perico

2
Luca: Bu doğru, ama önemli olan şey dev araçları kapatıldığında "önbelleği devre dışı bırak" etkisi yoktur. önbellek devre dışıysa ön kontrol istekleri yalnızca bir kez (elbette sunucu başına) gönderilir ve önbellek devre dışı bırakılmışsa her istekden önce gönderilir.
Nir

Bu gerçekten yardımcı oldu.
Anuraag Patil

38

Evet, seçenek isteğinden kaçınmak mümkündür. Herhangi bir veriyi başka bir alana gönderdiğinizde (yayınladığınızda) seçenekler isteği ön kontrol talebidir. Bu bir tarayıcı güvenliği sorunu. Ancak başka bir teknoloji kullanabiliriz: iframe taşıma katmanı. Herhangi bir CORS yapılandırmasını unutmanızı ve hazır çözümü kullanmanızı şiddetle tavsiye ederim ve her yerde çalışır.

Buraya bir göz atın: https://github.com/jpillora/xdomain

Ve çalışma örneği: http://jpillora.com/xdomain/


bu aslında bir çeşit bırakma vekili mi?
matanster

15
"Herhangi bir veriyi başka bir alana gönderdiğinizde (yayınladığınızda) seçenekler isteği ön kontrol talebidir." - Bu doğru değil. Ön kontrol isteğini tetiklemeden normal bir HTML formuyla gönderebileceğiniz herhangi bir POST isteğini göndermek için XHR'yi kullanabilirsiniz. Yalnızca bir formun yapamayacağı şeyleri yapmaya başladığınızda (özel içerik türleri veya ekstra istek başlıkları gibi) bir ön kontrol gönderilir.
Quentin

Buradaki çözüm, bazı durumlarda çalışan, ancak bazı büyük sınırlamalara sahip bir iframe şimasına güveniyor gibi görünüyor. Yanıtın HTTP durum kodunu veya başka bir HTTP yanıt üstbilgisinin değerini bilmek isterseniz ne olur?
Stephen Crosby

5
iFrames bunun için yapılmadı.
Romko

1
bu bir yazılım uygulamasıdır ve şu son soruyu yanıtlar: "Tarayıcının OPTIONS istekleri göndermesini tamamen durdurmanın bir yolu var mı?". Uzun lafın kısası, Mozilla veya Chromium'da devre dışı bırakmanın bir yolu yok, kodda uygulandı ve tek "çalışma" seçenekleri sadece davranışı atlattı.
çöpçü

15

Var olmasının nedenini anlayan ancak OPTIONS çağrılarını yetkisiz olarak ele almayan bir API'ye erişmesi gereken bir geliştirici için, geçici bir cevaba ihtiyacım var, bu yüzden API sahibi uygun SPA CORS desteği ekleyene veya bir proxy API alana kadar yerel olarak gelişebilirim faal ve çalışır durumda.

Safari'de CORS'u ve Mac'te Chrome'u devre dışı bırakabileceğinizi buldum.

Chrome'da aynı başlangıç ​​politikasını devre dışı bırak

Chrome: Chrome'dan çıkın, bir terminal açın ve şu komutu yapıştırın: open /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir

Safari: Safari'de aynı köken politikasını devre dışı bırakma

Safari'de aynı köken politikasını devre dışı bırakmak istiyorsanız (9.1.1 sürümüm var), yalnızca geliştirici menüsünü etkinleştirmeniz ve geliştirme menüsünden "Çapraz Kaynak Sınırlamalarını Devre Dışı Bırak" ı seçmeniz gerekir.


4
Sen diyor kısmına fazla vurguyu koymalıyız "Bu permament çözüm ASLA !!!!" . Aynı köken politikası çok önemli bir tarayıcı güvenlik önlemidir ve normalde internette gezinirken asla devre dışı bırakılmamalıdır.
jannis

Web'in bu şekilde çalışmasını diliyorum, bu yüzden ihtiyaç duyduğumuz verileri ekstra güçlük çekmeden sunuculardan talep edebiliriz.
jemiloii

14

Daha önceki yayınlarda da belirtildiği gibi, OPTIONSistekler bir nedenden dolayı var. Sunucunuzdan büyük yanıt süreleriyle ilgili bir sorununuz varsa (örn. Denizaşırı bağlantı), tarayıcınızın ön kontrol isteklerini önbelleğe almasını da sağlayabilirsiniz.

Sunucunuzun Access-Control-Max-Ageüstbilgiyle yanıt vermesini sağlayın ve aynı bitiş noktasına giden istekler için ön kontrol isteği önbelleğe alınmış ve artık gerçekleşmeyecektir.


1
Bunun için teşekkür ederim! OPTIONSBu başlık ile isteklerin önbelleğe alınacağı gerçeği, okuduğum tüm CORS belgelerinde oldukça opak.
joshperry

Ve önbellek yalnızca aynı URL ile yürürlüğe girer. Gidiş-dönüşleri gerçekten azaltabilecek bir alan adı düzeyinde ön kontrol önbelleği istiyorum. (CORS saçma!)
merak

8

Bu sorunu çözdüm.

if($_SERVER['REQUEST_METHOD'] == 'OPTIONS' && ENV == 'devel') {
    header('Access-Control-Allow-Origin: *');
    header('Access-Control-Allow-Headers: X-Requested-With');
    header("HTTP/1.1 200 OK");
    die();
}

Sadece geliştirme amaçlıdır. Bununla ben 8s ve 500ms değil 9ms ve 500ms bekliyorum. Üretim JS uygulaması üretim ile aynı makinede olacağı için bunu yapabilirim, bu yüzden hayır olacak OPTIONSama geliştirme benim yerelim.


4

Yapamazsınız ama JSONP kullanarak CORS'den kaçınabilirsiniz.


2
Bir OPTIONS isteği yalnızca basit olmayan bir şey yapıyorsanız alırsınız . JSONP ile yalnızca basit istekler (GET, özel üstbilgi yok, kimlik doğrulama verisi yok) yapabilirsiniz, bu nedenle JSONP burada yerini alamaz.
Quentin

Evet, biliyorum ama proje şartlarını tam olarak bilmiyorum. Biliyorum, basit bir kor önlemek ama projeye bağlı. En kötü senaryoda, CORS'den kaçınmak için get parametrelerini kullanarak veri iletmeniz gerekir. Yani, JSONP proje gereksinimlerine bağlı olarak korselerin yerini alabilir (dediğin gibi, basit istekler kullanarak)
Jose Mato

Uçuş öncesi sözde tasarımını anlamıyorum. İstemci sunucuya veri göndermeye karar verirse, güvenli olmamasına ne sebep olabilir? Tel üzerindeki yükü iki katına çıkarmanın bir anlamı olmadığını görmüyorum.
Qian Chen

@ElgsQianChen bu muhtemelen sorunuza cevap verebilir stackoverflow.com/questions/15381105/…
Leo Correa

0

Bir buçuk gün benzer bir problem üzerinde çalışmaya çalıştıktan sonra IIS ile ilgisi olduğunu gördüm .

Web API projem aşağıdaki gibi kuruldu:

// WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
    var cors = new EnableCorsAttribute("*", "*", "*");
    config.EnableCors(cors);
    //...
}

Web.config> system.webServer düğümünde pek çok yayında gördüğüm gibi CORS'ye özgü yapılandırma seçenekleri yoktu

Dekoratör olarak global.asax veya denetleyicide CORS'ye özgü kod yok

Sorun, uygulama havuzu ayarlarıdır .

Yönetilen boru hattı modu klasik (ayarlandı entegre olarak değiştirdim ) ve Kimlik ağ hizmeti olarak ayarlandı ( ApplicationPoolIdentity olarak değiştirdim )

Bu ayarları değiştirmek (ve uygulama havuzunu yenilemek) benim için düzeltti.


-2

Benim için işe yarayan, "github.com/gorilla/handlers" dosyasını içe aktarmak ve ardından şu şekilde kullanmaktı:

router := mux.NewRouter()
router.HandleFunc("/config", getConfig).Methods("GET")
router.HandleFunc("/config/emcServer", createEmcServers).Methods("POST")

headersOk := handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type"})
originsOk := handlers.AllowedOrigins([]string{"*"})
methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "OPTIONS"})

log.Fatal(http.ListenAndServe(":" + webServicePort, handlers.CORS(originsOk, headersOk, methodsOk)(router)))

Bir Ajax POST isteği yürüttüğümde ve ona JSON verileri eklediğimde, Chrome her zaman önceki AllowHeaders yapılandırmamda olmayan Content-Type başlığını eklerdi.


-2

Geçmişte kullandığım bir çözüm - sitenizin etkialanim.com'da olduğunu ve foreigndomain.com'a bir ajax isteği yapmanız gerektiğini varsayalım

Etki alanınızdan yabancı etki alanına IIS yeniden yazma yapılandırın - ör.

<rewrite>
  <rules>
    <rule name="ForeignRewrite" stopProcessing="true">
        <match url="^api/v1/(.*)$" />
        <action type="Rewrite" url="https://foreigndomain.com/{R:1}" />
    </rule>
  </rules>
</rewrite>

mydomain.com sitenizde - daha sonra aynı başlangıç ​​talebini yapabilirsiniz ve herhangi bir seçenek isteğine gerek yoktur :)


-2

İsteği kesen ve uygun başlıkları yazan bir proxy kullanılması durumunda çözülebilir. Özel Vernik durumunda, kurallar şöyle olacaktır:

if (req.http.host == "CUSTOM_URL" ) {
set resp.http.Access-Control-Allow-Origin = "*";
if (req.method == "OPTIONS") {
   set resp.http.Access-Control-Max-Age = "1728000";
   set resp.http.Access-Control-Allow-Methods = "GET, POST, PUT, DELETE, PATCH, OPTIONS";
   set resp.http.Access-Control-Allow-Headers = "Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since";
   set resp.http.Content-Length = "0";
   set resp.http.Content-Type = "text/plain charset=UTF-8";
   set resp.status = 204;
}

}


-5

Belki bir çözüm var (ama bunu test etmedim): uzak etki alanınızı etkinleştirmek için CSP (İçerik Güvenliği Politikası) kullanabilirsiniz ve tarayıcılar CORS OPTIONS istek doğrulamasını atlayabilir.

Biraz zaman bulursam, bunu test edeceğim ve bu yazıyı güncelleyeceğim!

CSP: https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Content-Security-Policy

CSP Özellikleri: https://www.w3.org/TR/CSP/


Az önce test ettim ve işe yaramıyor, xhr istek kabulü için CSP'den sonra CORS hala gerekli ...
Arnaud Tournier
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.