jQuery $ .ajax (), $ .post wysyła „OPCJE” jako REQUEST_METHOD w Firefox


330

Problem z tym, co uważałem za stosunkowo prostą wtyczkę jQuery ...

Wtyczka powinna pobrać dane ze skryptu php poprzez ajax, aby dodać opcje do <select>. Żądanie ajax jest dość ogólne:

$.ajax({
  url: o.url,
  type: 'post',
  contentType: "application/x-www-form-urlencoded",
  data: '{"method":"getStates", "program":"EXPLORE"}',
  success: function (data, status) {
    console.log("Success!!");
    console.log(data);
    console.log(status);
  },
  error: function (xhr, desc, err) {
    console.log(xhr);
    console.log("Desc: " + desc + "\nErr:" + err);
  }
});

Wygląda na to, że działa dobrze w Safari. W przeglądarce Firefox 3.5 REQUEST_TYPEna serwerze są zawsze „OPCJE”, a dane $ _POST nie są wyświetlane. Apache rejestruje żądanie jako typ „OPCJE”:

::1 - - [08/Jul/2009:11:43:27 -0500] "OPTIONS sitecodes.php HTTP/1.1" 200 46

Dlaczego to wywołanie ajax miałoby działać w przeglądarce Safari, ale nie w Firefoksie, i jak to naprawić w przeglądarce Firefox?

Nagłówki odpowiedzi
Data: środa, 08 lipca 2009 21:22:17 GMT
Serwer: Apache / 2.0.59 (Unix) PHP / 5.2.6 DAV / 2
X-Powered-By: PHP / 5.2.6
Długość treści 46
Limit czasu podtrzymania = 15, maks. = 100
Połączenie Keep-Alive
Content-Type text / html

Żądaj nagłówków
Formularz zamówienia hosta: 8888
User-Agent Mozilla / 5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv: 1.9.1) Gecko / 20090624 Firefox / 3.5
Akceptuj tekst / html, application / xhtml + xml, application / xml; q = 0,9, * / *; q = 0,8
Accept-Language en-us, en; q = 0,5
Zaakceptuj kodowanie gzip, deflate
Accept-Charset ISO-8859-1, utf-8; q = 0,7, *; q = 0,7
Keep-Alive 300
Połączenie utrzymuje się przy życiu
Pochodzenie http://ux.inetu.act.org
Kontrola dostępu-żądanie-metoda POST
Access-Control-Request-Headers x-Request-with

Oto zdjęcie wyjścia Firebuga:


Czy możesz opublikować odpowiedź firebug i zażądać nagłówków. Podczas uruchamiania podobnego kodu w przeglądarce Firefox nie pojawia się żaden błąd.
MitMaro,

Dodano informacje nagłówka i zdjęcie z Firebug.
fitzgeraldsteele

Właśnie miałem ten sam problem podczas wdrażania wbudowanego serwera WWW. Dzięki, że pytasz :)
Robert Gould,

Jeśli szukasz rozwiązań Java JAX-RS, zobacz tutaj: Access-Control-Allow-Origin
Tobias Sarnow

Wygląda na to, że Firefox się zmienił? Nie otrzymuję żadnych żądań opcji.
Buge

Odpowiedzi:


169

Przyczyną błędu są te same zasady pochodzenia. Pozwala tylko na wykonywanie zapytań XMLHTTP we własnej domenie. Sprawdź, czy możesz zamiast tego użyć wywołania zwrotnego JSONP :

$.getJSON( 'http://<url>/api.php?callback=?', function ( data ) { alert ( data ); } );

26
dlaczego Firefox jest jedyną przeglądarką, która to robi? Chcę posta nie dostać.
Masłów,

11
Crossite-POST: Czy ktoś zna rozwiązanie, aby wykonać POST z aplikacją / json jako Content-Type?
schoetbi

13
Więc jakie jest dokładnie rozwiązanie?
Nik So

3
Poszukiwanie rozwiązania tego i używanie getJSON zamiast wywoływania ajax nie robi tego dla mnie, ponieważ jest znacznie bardziej ograniczone.
Timo Wallenius,

1
@schoetbi, dla którego musisz użyć CORS, który jest dobrze obsługiwany w nowszych przeglądarkach ... ograniczona obsługa w IE8-9 i wymaga wsparcia po stronie serwera.
Tracker1,

57

Użyłem następującego kodu po stronie Django, aby zinterpretować żądanie OPTIONS i ustawić wymagane nagłówki kontroli dostępu. Po tym zaczęły działać moje żądania między domenami z Firefoksa. Jak wspomniano wcześniej, przeglądarka najpierw wysyła żądanie OPCJE, a następnie natychmiast POST / GET

def send_data(request):
    if request.method == "OPTIONS": 
        response = HttpResponse()
        response['Access-Control-Allow-Origin'] = '*'
        response['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
        response['Access-Control-Max-Age'] = 1000
        # note that '*' is not valid for Access-Control-Allow-Headers
        response['Access-Control-Allow-Headers'] = 'origin, x-csrftoken, content-type, accept'
        return response
    if request.method == "POST":
        # ... 

Edycja: wydaje się, że przynajmniej w niektórych przypadkach należy dodać te same nagłówki kontroli dostępu do rzeczywistej odpowiedzi. Może to być nieco mylące, ponieważ żądanie wydaje się skuteczne, ale Firefox nie przekazuje treści odpowiedzi do Javascript.


Twoja edycja dotycząca rzeczywistej odpowiedzi POST / GET jest nieco przerażająca; jeśli ktoś może to potwierdzić, daj nam znać tutaj!
Arjan

Nie wiem, czy jest to błąd, czy funkcja, ale wydaje się, że ktoś też to zauważył. Zobacz na przykład kodemaniak.de/?p=62 i wyszukaj „puste ciało odpowiedzi”
Juha Palomäki

2
Istnieje różnica między prostymi żądaniami a tymi, które wymagają inspekcji wstępnej. Twoje „rozwiązanie” będzie działało tylko z żądaniami inspekcji wstępnej, więc nie jest to prawdziwe rozwiązanie. Ilekroć w nagłówkach żądania pojawi się nagłówek „Origin:”, należy odpowiedzieć z tym dozwolonym.
odinho

1
Uważam, że nagłówek Access-Control-Allow-Headerspowinien zawierać wartość x-csrf-token, a nie x-csrftoken.
JellicleCat

16

W tym artykule Centrum deweloperów mozilla opisano różne scenariusze żądań między domenami. Artykuł wydaje się wskazywać, że żądanie POST z typem treści „application / x-www-form-urlencoded” powinno być wysyłane jako „proste żądanie” (bez żądania OPCJI „wstępnej kontroli”). Stwierdziłem jednak, że Firefox wysłał żądanie OPTIONS, mimo że mój test POST został wysłany z tym rodzajem treści.

Udało mi się to zrobić, tworząc moduł obsługi żądań opcji na serwerze, który ustawił nagłówek odpowiedzi „Access-Control-Allow-Origin” na „*”. Możesz być bardziej restrykcyjny, ustawiając go na coś konkretnego, np. „ Http://someurl.com ”. Przeczytałem również, że podobno można podać listę wielu źródeł oddzieloną przecinkami, ale nie udało mi się tego uruchomić.

Gdy Firefox otrzyma odpowiedź na żądanie OPCJE z akceptowalną wartością „Kontrola dostępu - Zezwalaj na pochodzenie”, wysyła żądanie POST.


15

Rozwiązałem ten problem za pomocą rozwiązania opartego całkowicie na Apache. W moim vhost / htaccess umieszczam następujący blok:

# enable cross domain access control
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS"

# force apache to return 200 without executing my scripts
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule .* / [R=200,L]

Ta druga część może nie być potrzebna, w zależności od tego, co się stanie, gdy Apache wykona skrypt docelowy. Zasługa przyjazny folk ServerFault do drugiej części.


Twoja odpowiedź pomogła mi, ale jeśli potrzebujesz logiki za CORS, nie rozwiązuje się ona całkowicie.
Ratata Tata,

10

Wydaje się, że ten PHP na górze odpowiadającego skryptu działa. (W przeglądarce Firefox 3.6.11. Nie przeprowadziłem jeszcze wielu testów).

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
header('Access-Control-Max-Age: 1000');
if(array_key_exists('HTTP_ACCESS_CONTROL_REQUEST_HEADERS', $_SERVER)) {
    header('Access-Control-Allow-Headers: '
           . $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']);
} else {
    header('Access-Control-Allow-Headers: *');
}

if("OPTIONS" == $_SERVER['REQUEST_METHOD']) {
    exit(0);
}

To może być kwestia gustu, ale zawsze wysyłając te nagłówki odpowiedzi (także GET, POST...) jest nieco zbyt wiele do mojego gustu. (I zastanawiam się, czy zawsze wysyłanie tych jest zgodne ze specyfikacjami?)
Arjan

3
zawiń go, jeśli ($ _ SERVER ['HTTP_ORIGIN']). Jeśli nagłówek tam jest, jest to żądanie CORS, jeśli nie, cóż, nie trzeba niczego wysyłać.
odinho

7

Miałem ten sam problem z wysyłaniem żądań do map Google, a rozwiązanie jest dość proste z jQuery 1.5 - do użytku z dataType dataType: "jsonp"


12
Niezgodny z metodą POST.
Pavel Vlasov

1
Działa z metodą GET, ale jest to bardzo ograniczone rozwiązanie. Na przykład nie można odesłać odpowiedzi z określonym nagłówkiem zawierającym token.
svassr

6

Culprit jest prośbą o przeprowadzenie inspekcji wstępnej przy użyciu metody OPTIONS

W przypadku metod żądań HTTP, które mogą powodować skutki uboczne na danych użytkownika (w szczególności w przypadku metod HTTP innych niż GET lub użycia POST z niektórymi typami MIME), specyfikacja wymaga, aby przeglądarki „wstępnie sprawdzały” żądanie, żądając obsługiwanych metod z serwer z metodą żądania OPCJE HTTP, a następnie, po „zatwierdzeniu” przez serwer, wysłanie rzeczywistego żądania za pomocą faktycznej metody żądania HTTP.

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

Rozwiązałem problem, dodając następujące wiersze w Nginx conf.

    location / {
               if ($request_method = OPTIONS ) {
                   add_header Access-Control-Allow-Origin  "*";
                   add_header Access-Control-Allow-Methods "POST, GET, PUT, UPDATE, DELETE, OPTIONS";
                   add_header Access-Control-Allow-Headers "Authorization";
                   add_header Access-Control-Allow-Credentials  "true";
                   add_header Content-Length 0;
                   add_header Content-Type text/plain;
                   return 200;
               }
    location ~ ^/(xxxx)$ {
                if ($request_method = OPTIONS) {
                    rewrite ^(.*)$ / last;
                }
    }

1
Ta odpowiedź jest bardzo pomocna, dzięki. Fakt, że przeglądarka wysyła żądanie inspekcji wstępnej metodą OPTIONS, nie jest oczywisty.
Normangorman,

4

Przeglądałem źródło 1.3.2, gdy korzystam z JSONP, żądanie jest tworzone przez dynamiczne budowanie elementu SCRIPT, który mija zasady przeglądarki w tej samej domenie. Oczywiście nie można wykonać żądania POST przy użyciu elementu SCRIPT, przeglądarka pobierze wynik za pomocą GET.

Podczas żądania wywołania JSONP element SCRIPT nie jest generowany, ponieważ robi to tylko wtedy, gdy typ wywołania AJAX jest ustawiony na GET.

http://dev.jquery.com/ticket/4690


3

Mieliśmy taki problem z ASP.Net. Nasz IIS $.postzwracał wewnętrzny błąd serwera podczas próby wykonania jQuery w celu uzyskania zawartości HTML ze względu na PageHandlerFactory był ograniczony do odpowiadania tylko na GET,HEAD,POST,DEBUGczasowniki. Możesz więc zmienić to ograniczenie, dodając czasownik „OPCJE” do listy lub wybierając „Wszystkie czasowniki”

Możesz to zmienić w Menedżerze IIS, wybierając swoją stronę internetową, a następnie wybierając Mapowania modułu obsługi, dwukrotnie kliknij PageHandlerFactory dla plików * .apx zgodnie z potrzebami (korzystamy ze zintegrowanej puli aplikacji w środowisku 4.0). Kliknij Żądaj ograniczeń, a następnie przejdź do Tabn czasowników i zastosuj modyfikację.

Teraz nasza $.postprośba działa zgodnie z oczekiwaniami :)


2

Sprawdź, czy actionadres URL formularza zawiera wwwczęść domeny, a otwarta strona oryginalna jest wyświetlana bez www.

Zazwyczaj wykonywane dla kanonicznych adresów URL ..

Walczyłem przez wiele godzin, zanim natknąłem się na ten artykuł i znalazłem ślad Cross Domain.


2

Wydaje mi się, że jeśli o.url = 'index.php'i ten plik istnieje, to jest w porządku i zwracam komunikat o powodzeniu w konsoli. Zwraca błąd, jeśli używam adresu URL:http://www.google.com

Jeśli wykonujesz żądanie postu, dlaczego nie użyć bezpośrednio metody $ .post :

$.post("test.php", { func: "getNameAndTime" },
    function(data){
        alert(data.name); // John
        console.log(data.time); //  2pm
    }, "json");

To jest o wiele prostsze.


Mam to samo z tym ... pomyślałem, że powinienem użyć $ .ajax (), abym mógł przynajmniej uzyskać informacje o debugowaniu na temat błędu.
fitzgeraldsteele


1

Rozwiązaniem tego jest:

  1. użyj dataType: json
  2. dodaj &callback=?do swojego adresu URL

działało to na wywołanie Facebook API i Firefox. Firebug używa GETzamiast OPTIONSpowyższych warunków (oba).




0

Spróbuj dodać opcję:

dataType: „json”


2
działało, dlaczego json jest uważany za „bezpieczny” dla żądań między domenami?
Nik So

0
 function test_success(page,name,id,divname,str)
{ 
 var dropdownIndex = document.getElementById(name).selectedIndex;
 var dropdownValue = document.getElementById(name)[dropdownIndex].value;
 var params='&'+id+'='+dropdownValue+'&'+str;
 //makerequest_sp(url, params, divid1);

 $.ajax({
    url: page,
    type: "post",
    data: params,
    // callback handler that will be called on success
    success: function(response, textStatus, jqXHR){
        // log a message to the console
        document.getElementById(divname).innerHTML = response;

        var retname = 'n_district';
        var dropdownIndex = document.getElementById(retname).selectedIndex;
        var dropdownValue = document.getElementById(retname)[dropdownIndex].value;
        if(dropdownValue >0)
        {
            //alert(dropdownValue);
            document.getElementById('inputname').value = dropdownValue;
        }
        else
        {
            document.getElementById('inputname').value = "00";
        }
        return;
        url2=page2; 
        var params2 = parrams2+'&';
        makerequest_sp(url2, params2, divid2);

     }
});         
}

Odpowiedź na to pytanie była już 6 miesięcy temu. Jak to rozwiązuje?
Barmar

0

Miałem podobny problem z próbą użycia API Facebooka.

Jedynym contentType, który nie wysłał żądania inspekcji wstępnej, wydawał się być tylko tekst / zwykły ... a nie reszta parametrów wymienionych tutaj w mozilli

  • Dlaczego to jedyna przeglądarka, która to robi?
  • Dlaczego Facebook nie zna i nie akceptuje wniosku o inspekcję wstępną?

FYI: Wspomniany dokument Moz sugeruje, że nagłówki X-Lori powinny wyzwalać żądanie inspekcji wstępnej ... tak nie jest.



0

Spróbuj dodać następujące:

dataType: "json",
ContentType: "application/json",
data: JSON.stringify({"method":"getStates", "program":"EXPLORE"}),  

0

Użyłem adresu URL serwera proxy, aby rozwiązać podobny problem, gdy chcę wysłać dane do mojego apache solr hostowanego na innym serwerze. (To może nie być idealna odpowiedź, ale rozwiązuje mój problem).

Śledź ten adres URL: Używając Mode-Rewrite do proxy , dodaję ten wiersz do mojego httpd.conf:

 RewriteRule ^solr/(.*)$ http://ip:8983/solr$1 [P]

Dlatego mogę po prostu wysyłać dane do / solr zamiast wysyłać dane do http: // ip: 8983 / solr / *. Następnie będzie publikować dane tego samego źródła.


0

Mam już ten kod dobrze radzący sobie z moją sytuacją cors w php:

header( 'Access-Control-Allow-Origin: '.CMSConfig::ALLOW_DOMAIN );
header( 'Access-Control-Allow-Headers: '.CMSConfig::ALLOW_DOMAIN );
header( 'Access-Control-Allow-Credentials: true' );

I działało dobrze lokalnie i zdalnie, ale nie do przesyłania w trybie zdalnym.

Coś się stało z apache / php LUB moim kodem, nie zadałem sobie trudu, aby go wyszukać, gdy poprosisz o OPCJE, zwraca mój nagłówek z regułami cors, ale z wynikiem 302. Dlatego moja przeglądarka nie rozpoznaje jako akceptowalnej sytuacji.

To, co zrobiłem, na podstawie odpowiedzi @ Mark McDonald, to po prostu wstawienie tego kodu za moim nagłówkiem:

if( $_SERVER['REQUEST_METHOD'] === 'OPTIONS' )
{
    header("HTTP/1.1 202 Accepted");
    exit;
}

Teraz, gdy poprosisz OPTIONS, wyśle ​​tylko nagłówek i wynik 202.


-1

Informujemy:

JSONP obsługuje tylko metodę żądania GET.

* Wyślij zapytanie przez firefox : *

$.ajax({
   type: 'POST',//<<===
   contentType: 'application/json',
   url: url,
   dataType: "json"//<<=============
    ...
});

Powyższe żądanie wysłane przez OPCJE (while ==> wpisz: „POST” ) !!!!

$.ajax({
    type: 'POST',//<<===
    contentType: 'application/json',
    url: url,
    dataType: "jsonp"//<<==============
    ...
});

Ale powyżej żądanie wysyłane przez GET (while ==> wpisz: „POST” ) !!!!

Podczas „komunikacji między domenami” zwracaj uwagę i zachowaj ostrożność.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.