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:

fitzgeraldsteele
źródło
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 ); } );
Jonas Skovmand
źródło
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.

Juha Palomäki
źródło
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.

Mike C.
źródło
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.

Mark McDonald
źródło
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);
}
Chad Clark
źródło
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"

słowiański
źródło
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;
                }
    }
thinkhy
źródło
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


źródło
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 :)

Fboiton
źródło
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.

Bijay Rungta
źródło
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.

Elzo Valugi
źródło
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).

Antonio Gulli
źródło
1

Inną możliwością obejścia problemu jest użycie skryptu proxy. Metodę tę opisano na przykład tutaj

Niehztog
źródło
0

Czy możesz tego spróbować bez

contentType:application/x-www-form-urlencoded

Mathias F.
źródło
Obawiam się, że ten sam wynik.
fitzgeraldsteele
0

Spróbuj dodać opcję:

dataType: „json”

ScottE
źródło
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);

     }
});         
}
Naser Gulzade
źródło
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.

Rysował
źródło
0

Musisz trochę popracować po stronie serwera. Widzę, że używasz PHP po stronie serwera, ale rozwiązanie dla aplikacji .NET jest tutaj: Nie można ustawić typu zawartości na 'application / json' w jQuery.ajax

Zrób to samo w skrypcie PHP i będzie działać. Po prostu: na pierwsze żądanie przeglądarka pyta serwer, czy wolno przesyłać takie dane tego typu, a drugie żądanie jest prawidłowe / dozwolone.

Fanda
źródło
0

Spróbuj dodać następujące:

dataType: "json",
ContentType: "application/json",
data: JSON.stringify({"method":"getStates", "program":"EXPLORE"}),  
Mary Jain
źródło
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.

Bernice
źródło
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.

Ratata Tata
źródło
-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ść.

M.Namjo
źródło