AJAX w Chrome wysyła OPCJE zamiast GET / POST / PUT / DELETE?

107

W pracy pracuję nad wewnętrzną aplikacją internetową. W IE10 żądania działają dobrze, ale w Chrome wszystkie żądania AJAX (których jest wiele) są wysyłane za pomocą OPCJI zamiast dowolnej zdefiniowanej metody, którą im daję. Technicznie rzecz biorąc, moje żądania są „między domenami”. Witryna jest obsługiwana przez localhost: 6120, a usługa, do której wysyłam żądania AJAX, znajduje się na 57124. Ten zamknięty błąd jquery definiuje problem, ale nie jest prawdziwą poprawką.

Co mogę zrobić, aby użyć właściwej metody http w żądaniach AJAX?

Edytować:

To jest w ładowaniu dokumentów na każdej stronie:

jQuery.support.cors = true;

Każdy AJAX jest zbudowany podobnie:

var url = 'http://localhost:57124/My/Rest/Call';
$.ajax({
    url: url,
    dataType: "json",
    data: json,
    async: true,
    cache: false,
    timeout: 30000,
    headers: { "x-li-format": "json", "X-UserName": userName },
    success: function (data) {
        // my success stuff
    },
    error: function (request, status, error) {
        // my error stuff
    },
    type: "POST"
});
Corey Ogburn
źródło
2
Ostatni komentarz w tym zgłoszeniu błędu wyjaśnia to całkiem dobrze ...
Kevin B
1
To zmieniło mój umysł, ponieważ wszystko, co robię, jest tak waniliowe (a mój kod jest podobny do tego z błędu jquery). Pomijając to, nie ma wymówki, aby tego nie uwzględnić. BRB, pobieram przykładowy kod.
Corey Ogburn
3
Należy zauważyć, że IE nie bierze pod uwagę numerów portów podczas określania, czy żądanie pochodzi z innych źródeł.
Ray Nicholus
@KevinB: Nasza usługa REST korzysta z różnych żądań, wykonując różne czynności w oparciu o metodę http. Przełączenie wszystkiego na GET nie jest poprawną poprawką. Ponadto, zgodnie z odpowiedzią Dark Falcon, to i tak nie pomoże, ponieważ mam X-UserName i inne niestandardowe nagłówki w żądaniach.
Corey Ogburn
nie zmienia to faktu, że jeśli chcesz wysłać żądanie z różnych źródeł, musisz przestrzegać wszystkich reguł, które mają zastosowanie do żądań z innych źródeł, aby działało poprawnie. żądania cross-origin zazwyczaj obejmują żądanie OPTIONS. Postępuj z nim prawidłowo, a problem zniknie. Jedynym innym sposobem rozwiązania tego problemu (bez zmiany interfejsu API) jest posiadanie skryptu na tym samym serwerze, na którym znajduje się strona główna, który współdziała z interfejsem API przy użyciu kodu po stronie serwera.
Kevin B

Odpowiedzi:

136

Chrome wstępnie sprawdza żądanie wyszukania nagłówków CORS . Jeśli żądanie zostanie zaakceptowane, wyśle ​​prawdziwe żądanie. Jeśli robisz to w wielu domenach, będziesz musiał po prostu sobie z tym poradzić lub znaleźć sposób, aby żądanie nie było międzydomenowe. Dlatego błąd jQuery został zamknięty jako nie do naprawienia. Jest to zgodne z projektem.

W przeciwieństwie do prostych żądań (omówionych powyżej), żądania „wstępnie sprawdzone” najpierw wysyłają żądanie HTTP metodą OPTIONS do zasobu w innej domenie, aby określić, czy rzeczywiste żądanie jest bezpieczne do wysłania. Żądania między witrynami są wstępnie analizowane w ten sposób, ponieważ mogą mieć wpływ na dane użytkownika. W szczególności wniosek jest wstępnie sprawdzany, jeśli:

  • Używa metod innych niż GET, HEAD lub POST. Ponadto, jeśli POST jest używany do wysyłania danych żądania z typem zawartości innym niż application / x-www-form-urlencoded, multipart / form-data lub text / plain, np. Jeśli żądanie POST wysyła ładunek XML do serwera przy użyciu application / xml lub text / xml, a następnie żądanie jest wstępnie sprawdzane.
  • Ustawia niestandardowe nagłówki w żądaniu (np. Żądanie używa nagłówka, takiego jak X-PINGOTHER)
Dark Falcon
źródło
20
Niestandardowe nagłówki. Prawdopodobnie właśnie to uruchamia wywołania OPCJI inspekcji wstępnej.
Corey Ogburn
18

Na podstawie tego, że żądanie nie jest wysyłane na domyślnym porcie 80/443, to wywołanie Ajax jest automatycznie uznawane za żądanie zasobów między źródłami (CORS) , co innymi słowy oznacza, że ​​żądanie automatycznie wysyła żądanie OPTIONS, które sprawdza Nagłówki CORS po stronie serwera / serwletu.

Dzieje się tak, nawet jeśli ustawisz

crossOrigin: false;

lub nawet jeśli to pominiesz.

Powód jest po prostu taki localhost != localhost:57124. Spróbuj wysłać go tylko localhostbez portu - nie powiedzie się, ponieważ żądany cel nie będzie osiągalny, jednak zauważ, że jeśli nazwy domen są równe, żądanie jest wysyłane bez żądania OPTIONS przed POST.

Spadkowicz
źródło
3

Zgadzam się z Kevinem B. Raport o błędzie mówi wszystko. Wygląda na to, że próbujesz wykonywać połączenia AJAX między domenami. Jeśli nie znasz tej samej zasady pochodzenia, możesz zacząć tutaj: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Same_origin_policy_for_JavaScript .

Jeśli nie ma to być wywołanie AJAX między domenami, spróbuj ustawić docelowy adres URL jako względny i zobacz, czy problem zniknie. Jeśli naprawdę jesteś zdesperowany, zajrzyj do JSONP, ale uważaj, czai się chaos. Naprawdę niewiele więcej możemy zrobić, aby ci pomóc.

jgitter
źródło
1
Nie mogę zmienić struktury naszego systemu. Użycie innego portu jest wymogiem naszej architektury. Mam tę samą politykę pochodzenia, ale pomyślałem, że zaimplementowany przez nas CORS wystarczy. Najwyraźniej nie.
Corey Ogburn
2
Jeśli Twój serwer zwraca odpowiedzi JSON, możesz zajrzeć do metody JSONP, po prostu używaj jej odpowiedzialnie.
jgitter
1
Nie chcę się z tobą kłócić, ale JSONP używa tagów skryptu do pobierania danych z innej domeny, a następnie wysyła wynik do funkcji zwrotnej. Jest to o wiele trudniejsze, jeśli wynik nie jest json.
jgitter
1
Nie, to nie jest dużo trudniejsze. W rzeczywistości odpowiedź w żadnym przypadku nie powinna być poprawnym formatem JSON. Zamiast tego, serwer powinien zwrócić coś takiego: callbackfunc(somedata). Jak widać, to nie jest poprawny format JSON. I somedatamoże to być ciąg, liczba lub cokolwiek chcesz.
Ray Nicholus
1
Używam programu Postman i tam metody żądania są wysyłane poprawnie (np. „PUT”, „DELETE” itp.). Ale kiedy próbuję to zrobić z mojego kodu, zawsze wysyła je metodą żądania OPCJE. Nie mam pojęcia, jak Postman jest w stanie to zrobić.
ErwinGO
1

Jeśli to możliwe, przekaż parametry przez zwykłe GET / POST z inną nazwą i pozwól, aby kod po stronie serwera obsługiwał to.

Miałem podobny problem z moim własnym proxy, aby ominąć CORS i otrzymałem ten sam błąd POST-> OPTION w Chrome. To był Authorizationnagłówek w moim przypadku ( "x-li-format"i "X-UserName"tutaj w twoim przypadku). Skończyło się na przekazaniu go w formacie fikcyjnym (np. AuthorizatinJackW GET) i zmieniłem kod mojego serwera proxy, aby przekształcił go w nagłówek podczas wykonywania połączenia do miejsca docelowego . Tutaj jest w PHP:

if (isset($_GET['AuthorizationJack'])) {
    $request_headers[] = "Authorization: Basic ".$_GET['AuthorizationJack'];
}
Aidin
źródło
1

W moim przypadku wywołuję API hostowane przez AWS (API Gateway). Wystąpił błąd, gdy próbowałem wywołać interfejs API z domeny innej niż własna domena API. Ponieważ jestem właścicielem interfejsu API, włączyłem CORS dla środowiska testowego, zgodnie z opisem w dokumentacji Amazon .

W produkcji ten błąd nie wystąpi, ponieważ żądanie i api będą w tej samej domenie.

Mam nadzieję, że to pomoże!

gbonesso
źródło
0

Jak odpowiedział @Dark Falcon, po prostu sobie z tym poradziłem .

W moim przypadku używam serwera node.js i tworzę sesję, jeśli nie istnieje. Ponieważ metoda OPTIONS nie zawiera szczegółów sesji, ostatecznie utworzyła nową sesję dla każdego żądania metody POST.

Tak więc w mojej procedurze tworzenia sesji, jeśli nie istnieje, właśnie dodałem sprawdzenie, czy metoda jest OPTIONS, a jeśli tak, po prostu pomiń część tworzenia sesji:

    app.use(function(req, res, next) {
        if (req.method !== "OPTIONS") {
            if (req.session && req.session.id) {
                 // Session exists
                 next();
            }else{
                 // Create session
                 next();
          }
        } else {
           // If request method is OPTIONS, just skip this part and move to the next method.
           next(); 
        }
    }
Mahesh
źródło
0

Żądania „wstępnie sprawdzone” najpierw wysyłają żądanie HTTP metodą OPTIONS do zasobu w innej domenie, aby określić, czy rzeczywiste żądanie można bezpiecznie wysłać. Żądania między witrynami

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

Noorullah
źródło
1
Czy mógłbyś dodać trochę więcej informacji? Twoja odpowiedź wygląda jak komentarz. :)
Badacadabra
0

Rozważ użycie axios

axios.get( url,
{ headers: {"Content-Type": "application/json"} } ).then( res => {

  if(res.data.error) {

  } else { 
    doAnything( res.data )
  }

}).catch(function (error) {
   doAnythingError(error)
});

Miałem ten problem przy użyciu pobierania i axios działał idealnie.

Evhz
źródło
5
Axios używa również pierwszych OPCJI
Skylin R
0

Napotkałem bardzo podobny problem. Spędziłem prawie pół dnia, aby zrozumieć, dlaczego wszystko działa poprawnie w Firefoksie, a zawodzi w Chrome. W moim przypadku było to spowodowane zduplikowanymi (lub może błędnie wpisanymi) polami w nagłówku mojego żądania.

Andrew Tatomyr
źródło
0

Użyj pobierania zamiast XHR, wtedy żądanie nie zostanie podświetlone, nawet jeśli jest między domenami.

Fei Sun
źródło
-1
 $.ajax({
            url: '###',
            contentType: 'text/plain; charset=utf-8',
            async: false,
            xhrFields: {
                withCredentials: true,
                crossDomain: true,
                Authorization: "Bearer ...."
            },

            method: 'POST',

            data: JSON.stringify( request ),
            success: function (data) {
                console.log(data);
            }
        });

contentType: 'text / plain; charset = utf-8 'lub po prostu contentType:' text / plain ', działa dla mnie! pozdrowienia!!

David Lopes
źródło
Co to w ogóle ma wspólnego z pytaniem?
Corey Ogburn
Cześć, myślę, że to rozwiązuje problem w tytule, z tym typem treści przekazujesz metodę OPCJE. Pozdrawiam
David Lopes
ContentType nie ma nic wspólnego z metodą.
Corey Ogburn
Wiem, co mówisz, ale spróbuj. w zależności od przeglądarki rodzaj zawartości może wpłynąć na Twoje żądanie i zmienić metodę!
David Lopes