Dlaczego wysyłane jest żądanie OPTIONS i czy mogę je wyłączyć?

415

Buduję internetowy interfejs API. Zauważyłem, że za każdym razem, gdy używam Chrome do POST, GET do mojego interfejsu API, przed rzeczywistym żądaniem zawsze jest wysyłane żądanie OPTIONS, co jest dość denerwujące. Obecnie zmuszam serwer do ignorowania żądań OPTIONS. Teraz mam pytania, co jest dobre, aby wysłać żądanie OPCJE, aby podwoić obciążenie serwera? Czy jest jakiś sposób, aby całkowicie powstrzymać przeglądarkę przed wysyłaniem żądań OPTIONS?

Qian Chen
źródło

Odpowiedzi:

376

edytuj 13.09.2018 : dodano pewne szczegóły dotyczące tej prośby przed lotem i sposobu jej uniknięcia na końcu tej odpowiedzi.

OPTIONSżądania są tym, co nazywamy pre-flightżądaniami Cross-origin resource sharing (CORS).

Są one niezbędne, gdy wysyłasz żądania z różnych źródeł w określonych sytuacjach.

To żądanie przed lotem jest wysyłane przez niektóre przeglądarki jako środek bezpieczeństwa, aby upewnić się, że wykonane żądanie jest zaufane przez serwer. Oznacza to, że serwer rozumie, że metoda, pochodzenie i nagłówki wysyłane na żądanie są bezpieczne.

Twój serwer nie powinien ignorować, ale obsłużyć te żądania za każdym razem, gdy próbujesz wykonać żądania krzyżowego pochodzenia.

Dobry zasób można znaleźć tutaj http://enable-cors.org/

Jednym ze sposobów radzenia sobie z nimi w celu zapewnienia wygody jest upewnienie się, że dla dowolnej ścieżki z OPTIONSmetodą serwer wysyła odpowiedź z tym nagłówkiem

Access-Control-Allow-Origin: *

To powie przeglądarce, że serwer jest gotów odpowiedzieć na żądania z dowolnego źródła.

Aby uzyskać więcej informacji na temat dodawania obsługi CORS na serwerze, zobacz poniższy schemat blokowy

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

Schemat blokowy CORS


edycja 2018-09-13

OPTIONSŻądanie CORS jest uruchamiane tylko w niektórych przypadkach, jak wyjaśniono w dokumentach MDN :

Niektóre żądania nie wyzwalają inspekcji wstępnej CORS. Są to tak zwane „proste żądania” w tym artykule, chociaż specyfikacja Fetch (która definiuje CORS) nie używa tego terminu. Żądanie, które nie uruchamia inspekcji wstępnej CORS - tak zwane „proste żądanie” - to takie, które spełnia wszystkie następujące warunki:

Jedynymi dozwolonymi metodami są:

  • DOSTAĆ
  • GŁOWA
  • POCZTA

Oprócz nagłówków ustawianych automatycznie przez agenta użytkownika (na przykład Connection, User-Agent lub dowolny z innych nagłówków o nazwach zdefiniowanych w specyfikacji Fetch jako „zabroniona nazwa nagłówka”), jedynymi nagłówkami, które mogą być ręcznie ustawione są te, które specyfikacja Fetch definiuje jako „nagłówek żądania zabezpieczonego przez CORS”, które są:

  • Zaakceptować
  • Zaakceptuj język
  • Język treści
  • Typ zawartości (ale zwróć uwagę na dodatkowe wymagania poniżej)
  • DPR
  • Łącze w dół
  • Zapisz dane
  • Szerokość rzutni
  • Szerokość

Jedynymi dozwolonymi wartościami nagłówka Content-Type są:

  • application / x-www-form-urlencoded
  • multipart / form-data
  • Zwykły tekst

Żadne detektory zdarzeń nie są zarejestrowane w żadnym obiekcie XMLHttpRequestUpload używanym w żądaniu; są one dostępne za pomocą właściwości XMLHttpRequest.upload.

Żądanie nie zawiera obiektu ReadableStream.

Leo Correa
źródło
8
Ale ustawienie tej flagi Chrome nie jest realistyczne dla wszystkich użytkowników.
Qian Chen,
37
Niepoprawne jest twierdzenie, że prośby o przeprowadzenie inspekcji wstępnej są wymagane podczas składania wniosków dotyczących pochodzenia. Żądania inspekcji wstępnej są wymagane tylko w określonych sytuacjach, np. Jeśli ustawiasz niestandardowe nagłówki lub wysyłasz żądania inne niż get, head i post.
Robin Clowers
4
Co zabawne, przy składaniu żądania CORS za pomocą jQuery biblioteka JavaScript w szczególności unika ustawiania niestandardowego nagłówka, a także słowa ostrzeżenia dla programistów: w przypadku żądań międzydomenowych postrzeganie warunków wstępnych jest podobne do układanki po prostu nigdy nie ustawiaj tego dla pewności.
Poniżej radaru
3
Jak to się dzieje, że jeśli zrobię a curldo interfejsu API, to działa, ale gdy uruchamiam z Chrome, pojawia się błąd?
SuperUberDuper,
5
@SuperUberDuper, ponieważ żądania CORS i inspekcji wstępnej są związane z przeglądarką. Możesz symulować CORS, dodając Originnagłówek do żądania, aby zasymulować, jakby żądanie pochodziło od określonego hosta (np. Twojastrona.com). Możesz także symulować żądania inspekcji wstępnej, ustawiając metodę HTTP żądania OPTIONSi Access-Control-*nagłówków
Leo Correa,
234

Przeszedłem przez ten problem, poniżej jest mój wniosek na ten temat i moje rozwiązanie.

Zgodnie ze strategią CORS (zdecydowanie zalecamy przeczytanie o niej) Nie możesz po prostu zmusić przeglądarki do zaprzestania wysyłania żądania OPCJE, jeśli uzna to za konieczne.

Istnieją dwa sposoby obejścia tego:

  1. Upewnij się, że Twoja prośba jest „prostą prośbą”
  2. Ustaw Access-Control-Max-Agedla żądania OPCJE

Proste zapytanie

Proste zapytanie krzyżowe to takie, które spełnia wszystkie następujące warunki:

Jedynymi dozwolonymi metodami są:

  • DOSTAĆ
  • GŁOWA
  • POCZTA

Oprócz nagłówków ustawianych automatycznie przez agenta użytkownika (np. Connection, User-Agent itp.), Jedynymi nagłówkami, które można ustawiać ręcznie, są:

  • Zaakceptować
  • Zaakceptuj język
  • Język treści
  • Typ zawartości

Jedynymi dozwolonymi wartościami nagłówka Content-Type są:

  • application / x-www-form-urlencoded
  • multipart / form-data
  • Zwykły tekst

Proste zapytanie nie spowoduje zgłoszenia OPCJI przed lotem.

Ustaw pamięć podręczną dla testu OPCJE

Możesz ustawić Access-Control-Max-Ageżądanie OPCJE, aby nie sprawdzało uprawnienia ponownie, dopóki nie wygaśnie.

Access-Control-Max-Age podaje wartość w sekundach, na jak długo można buforować odpowiedź na żądanie inspekcji wstępnej bez wysyłania kolejnej prośby inspekcji wstępnej.

Notowane ograniczenie

  • Chrome, sekundach maksymalnego do Access-Control-Max-AgeIs 600, która wynosi 10 minut, zgodnie z chromem kodu źródłowego
  • Access-Control-Max-Agedziała tylko dla jednego zasobu za każdym razem, na przykład GETżądania z tą samą ścieżką URL, ale różne zapytania będą traktowane jako różne zasoby. Zatem żądanie do drugiego zasobu nadal wyzwala żądanie inspekcji wstępnej.
Neekey
źródło
3
Tak ... to powinna być zaakceptowana odpowiedź i najbardziej odpowiednia do pytania…!
Rajesh Mbm
7
Dziękuję za wzmiankę Access-Control-Max-Age. To jest klucz tutaj. Pomaga to uniknąć nadmiernych żądań inspekcji wstępnej.
Idris Mokhtarzada
Korzystam z axios, aby zadzwonić do żądania. Gdzie mogę ustawić Access-Control-Max-Age w żądaniu axios?
Mohit Shah,
+1 Kluczem jest tutaj nagłówek Access-Control-Max-Age. To powinna być zaakceptowana odpowiedź! Ustawiłem 86400 sekund (24 godziny) na nagłówku i prośba o wstępną weryfikację zniknęła!
revobtz
1
@VitalyZdanevich nie! Nie unikaj application/jsontylko dlatego, że sprawia, że ​​twoje zapytanie nie jest „proste” (a tym samym uruchamia CORS). Przeglądarka wykonuje swoją pracę. Ustaw serwer tak, aby zwracał coś takiego jak nagłówek, Access-Control-Max-Age: 86400a przeglądarka nie wyśle ​​ponownie żądania OPCJE przez 24 godziny.
colm.anseo
139

Proszę odnieść się do tej odpowiedzi na temat faktycznego zapotrzebowania na OPCJE przed lotem: CORS - Jaka jest motywacja do wprowadzenia wniosków o lot wstępny?

Aby wyłączyć żądanie OPTIONS, należy spełnić poniższe warunki dla żądania ajax:

  1. Żądanie nie ustawia niestandardowych nagłówków HTTP, takich jak „application / xml” lub „application / json” itp
  2. Metodą żądania musi być GET, HEAD lub POST. Jeśli POST wpisz treść powinna być jednym z application/x-www-form-urlencoded, multipart/form-datalubtext/plain

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

device_exec
źródło
14
+1 za „niestandardowe nagłówki HTTP”! W moim przypadku powodowały one uruchomienie żądania przed lotem. Zreformowałem prośbę o wysłanie wszystkiego, co wysyłałem w nagłówkach, ponieważ treść żądania i żądania OPTIONS przestały być wysyłane.
Andre
21
application/xmllub application/jsonnie są „niestandardowymi nagłówkami HTTP”. Sam nagłówek byłby, Content-Typea nazwanie go „niestandardowym” byłoby mylące.
Leo Correa
1
Usunięto niestandardowe nagłówki HTTP i działało to jak urok!
Tim D
47

Gdy masz otwartą konsolę debugowania i Disable Cachewłączoną opcję, żądania inspekcji wstępnej będą zawsze wysyłane (tj. Przed każdym żądaniem). jeśli nie wyłączysz pamięci podręcznej, żądanie przed lotem zostanie wysłane tylko raz (na serwer)

Nir
źródło
3
o co ja myślę. debugowanie godzin to było moje rozwiązanie. pamięć podręczna wyłączona z powodu debugowania konsoli.
mauris
1
Nawet jeśli konsola debugowania jest zamknięta, wysyłane są żądania inspekcji wstępnej
Luca Perico
2
Luca: to prawda, ale chodzi o to, że „wyłącz pamięć podręczną” nie działa, gdy narzędzia programistyczne są zamknięte. żądania inspekcji wstępnej są wysyłane tylko raz (oczywiście na serwer), jeśli pamięć podręczna nie jest wyłączona i są wysyłane przed każdym żądaniem, jeśli pamięć podręczna jest wyłączona.
Nir,
To było bardzo pomocne.
Anuraag Patil
38

Tak, można uniknąć żądania opcji. Żądanie opcji to żądanie wstępnego sprawdzenia, gdy wysyłasz (publikujesz) dowolne dane do innej domeny. Jest to problem bezpieczeństwa przeglądarki. Możemy jednak użyć innej technologii: warstwy transportowej iframe. Zdecydowanie zalecam zapomnienie o jakiejkolwiek konfiguracji CORS i skorzystanie z gotowego rozwiązania, które będzie działać wszędzie.

Spójrz tutaj: https://github.com/jpillora/xdomain

I działający przykład: http://jpillora.com/xdomain/

XTRUST.ORG
źródło
czy to właściwie rodzaj zastępczego proxy?
matanster
15
„Żądanie opcji to żądanie inspekcji wstępnej, gdy wysyłasz (publikujesz) dowolne dane do innej domeny.” - To nieprawda. Za pomocą XHR można wysyłać dowolne żądania POST, które można wysłać zwykłym formularzem HTML, bez wyzwalania żądania wstępnego. Inspekcja wstępna jest wysyłana tylko wtedy, gdy zaczynasz robić rzeczy, których nie może zrobić formularz (takie jak niestandardowe typy treści lub dodatkowe nagłówki żądania).
Quentin,
Wydaje się, że tutaj rozwiązanie opiera się na podkładce iframe, która działa w niektórych przypadkach, ale ma pewne poważne ograniczenia. Co się stanie, jeśli chcesz poznać kod statusu HTTP odpowiedzi lub wartość innego nagłówka odpowiedzi HTTP?
Stephen Crosby
5
iFrame nie zostały do ​​tego stworzone.
Romko
1
jest to implementacja oprogramowania i odpowiada na ostatnie pytanie: „Czy jest jakiś sposób, aby całkowicie powstrzymać przeglądarkę przed wysyłaniem żądań OPTIONS?”. Krótko mówiąc, nie ma sposobu, aby wyłączyć go w Mozilli lub Chromium, jest zaimplementowany w kodzie, a jedyne „działające” opcje po prostu omijają to zachowanie.
padlinożerca
15

Dla programisty, który rozumie powód, dla którego istnieje, ale musi uzyskać dostęp do interfejsu API, który nie obsługuje wywołań OPTIONS bez autoryzacji, potrzebuję tymczasowej odpowiedzi, aby móc rozwijać się lokalnie, dopóki właściciel interfejsu API nie doda odpowiedniej obsługi SPA CORS lub nie otrzymam interfejsu API proxy gotowy do pracy.

Odkryłem, że możesz wyłączyć CORS w Safari i Chrome na komputerze Mac.

Wyłącz zasady tego samego pochodzenia w Chrome

Chrome: Zamknij Chrome, otwórz terminal i wklej to polecenie: open /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir

Safari: Wyłączanie zasad tego samego pochodzenia w Safari

Jeśli chcesz wyłączyć zasady tego samego pochodzenia w przeglądarce Safari (mam wersję 9.1.1), musisz tylko włączyć menu programisty i wybrać opcję „Wyłącz ograniczenia między źródłami” z menu programowania.

Joseph Juhnke
źródło
4
Powinieneś położyć większy nacisk na tę część, która mówi „to NIGDY nie powinno być trwałym rozwiązaniem !!!!” . Ta sama zasada pochodzenia jest bardzo ważnym środkiem bezpieczeństwa przeglądarki i nigdy nie powinna być wyłączana podczas normalnego przeglądania Internetu.
jannis
Chciałbym, żeby sieć po prostu działała w ten sposób, abyśmy mogli żądać potrzebnych nam danych z serwerów bez dodatkowych problemów.
jemiloii
14

Jak już wspomniano w poprzednich postach, OPTIONSprośby istnieją z jakiegoś powodu. Jeśli masz problem z długimi czasami reakcji z serwera (np. Połączenie zagraniczne), możesz również poprosić przeglądarkę o buforowanie żądań inspekcji wstępnej.

Poproś serwer o odpowiedź z Access-Control-Max-Agenagłówkiem, a w przypadku żądań kierowanych do tego samego punktu końcowego żądanie kontroli wstępnej zostanie zapisane w pamięci podręcznej i nie będzie już występować.

enpenax
źródło
1
Dziękuję Ci za to! Fakt, że OPTIONSżądania będą buforowane z tym nagłówkiem, jest dość nieprzejrzysty we wszystkich dokumentach CORS, które przeczytałem.
joshperry
Pamięć podręczna działa tylko z tym samym adresem URL. Chcę pamięci podręcznej inspekcji wstępnej na poziomie domeny, która może naprawdę zmniejszyć liczbę podróży w obie strony. (CORS jest głupi!)
zastanawiam się
8

Rozwiązałem ten problem jak.

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();
}

To jest tylko dla rozwoju. Z tym czekam 9ms i 500ms, a nie 8s i 500ms. Mogę to zrobić, ponieważ produkcyjna aplikacja JS będzie na tym samym komputerze co produkcja, więc nie będzie jej, OPTIONSale programowanie jest moje lokalne.

AntiCZ
źródło
4

Nie możesz, ale możesz uniknąć CORS za pomocą JSONP.

Jose Mato
źródło
2
Otrzymasz prośbę o OPCJE tylko wtedy, gdy robisz coś niełatwego . Za pomocą JSONP możesz składać tylko proste żądania (GET, żadnych niestandardowych nagłówków, żadnych danych uwierzytelniających), więc JSONP nie może tutaj zastąpić.
Quentin
Tak, wiem o tym, ale nie znam dokładnie wymagań projektu. Wiem, że to nie jest proste unikanie corsów, ale zależy to od projektu. W najgorszym scenariuszu, aby uniknąć CORS, musisz przekazać dane przy użyciu parametrów get. JSONP może więc zastąpić korki w zależności od wymagań projektu (jak powiedziałeś, używając prostych próśb)
Jose Mato
Nie rozumiem projektu tak zwanego przedlotu. Co może być niebezpieczne, jeśli klient zdecyduje się wysłać dane na serwer? Nie widzę sensu, aby podwoić obciążenie drutu.
Qian Chen
@ElgsQianChen to prawdopodobnie może odpowiedzieć na twoje pytanie stackoverflow.com/questions/15381105/...
Leo Correa
0

Po spędzeniu półtora dnia na próbach rozwiązania podobnego problemu odkryłem, że ma to związek z IIS .

Mój projekt interfejsu API sieci Web został skonfigurowany w następujący sposób:

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

Nie miałem specyficznych dla CORS opcji konfiguracji w węźle web.config> system.webServer, jak widziałem w tak wielu postach

Brak kodu specyficznego dla CORS w pliku global.asax lub w kontrolerze jako dekorator

Problemem były ustawienia puli aplikacji .

Udało tryb rurociąg został ustawiony na klasyczny ( zmienił go na zintegrowany ) i tożsamość została ustalona do usługi sieciowej ( zmienił go do ApplicationPoolIdentity )

Zmiana tych ustawień (i odświeżenie puli aplikacji) naprawiła to dla mnie.

Ju66ernaut
źródło
-2

Dla mnie działało zaimportowanie „github.com/gorilla/handlers”, a następnie użycie go w następujący sposób:

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)))

Gdy tylko wykonam żądanie POST Ajax i dołączę do niego dane JSON, Chrome zawsze doda nagłówek Content-Type, który nie był w mojej poprzedniej konfiguracji Dozwolonych Kierowników.

Kurt
źródło
-2

Jedno rozwiązanie, z którego korzystałem w przeszłości - załóżmy, że Twoja witryna znajduje się w domenie mydomain.com, i musisz wysłać zapytanie ajax do foreigndomain.com

Skonfiguruj przepisywanie IIS z Twojej domeny na domenę obcą - np

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

w witrynie mydomain.com - możesz wtedy złożyć to samo żądanie pochodzenia i nie ma potrzeby żądania opcji :)

David McEleney
źródło
-2

Można to rozwiązać w przypadku użycia serwera proxy, który przechwytuje żądanie i zapisuje odpowiednie nagłówki. W szczególnym przypadku Lakieru byłyby to zasady:

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;
}

}

Rafa Cuestas
źródło
-5

Być może istnieje rozwiązanie (ale go nie przetestowałem): możesz użyć CSP (Content Security Policy), aby włączyć domenę zdalną, a przeglądarki mogą pominąć weryfikację żądania CORS OPTIONS.

Jeśli znajdę trochę czasu, przetestuję to i zaktualizuję ten post!

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

Specyfikacja CSP: https://www.w3.org/TR/CSP/

Arnaud Tournier
źródło
Właśnie przetestowałem i to nie działa, CORS jest nadal wymagany po CSP dla przyjęcia wniosku o przyjęcie xhr ...
Arnaud Tournier