Żądanie CORS POST działa z prostego JavaScript, ale dlaczego nie z jQuery?

88

Próbuję wysłać prośbę o przesyłkę pocztową Cross Origin i działam w JavaScriptnastępujący sposób:

var request = new XMLHttpRequest();
var params = "action=something";
request.open('POST', url, true);
request.onreadystatechange = function() {if (request.readyState==4) alert("It worked!");};
request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
request.setRequestHeader("Content-length", params.length);
request.setRequestHeader("Connection", "close");
request.send(params);

Ale chciałbym użyć jQuery, ale nie mogę zmusić go do pracy. Oto, czego próbuję:

$.ajax(url, {
    type:"POST",
    dataType:"json",
    data:{action:"something"}, 
    success:function(data, textStatus, jqXHR) {alert("success");},
    error: function(jqXHR, textStatus, errorThrown) {alert("failure");}
});

Skutkuje to niepowodzeniem. Jeśli ktoś wie, dlaczego jQuerynie działa, daj nam znać. Dzięki.

(Używam wersji jQuery1.5.1 i Firefox 4.0, a mój serwer odpowiada odpowiednim Access-Control-Allow-Originnagłówkiem)

Magmowy
źródło
To było rozwiązanie dla mnie (użyj XMLHttpRequest Javascript) w obliczu problemów z CORS w Ionic Framework 3.
jeudyx

Odpowiedzi:

73

AKTUALIZACJA: Jak zauważył TimK, nie jest to już potrzebne w jquery 1.5.2. Ale jeśli chcesz dodać niestandardowe nagłówki lub zezwolić na użycie poświadczeń (nazwa użytkownika, hasło lub pliki cookie itp.), Czytaj dalej.


Myślę, że znalazłem odpowiedź! (4 godziny i dużo przekleństw później)

//This does not work!!
Access-Control-Allow-Headers: *

Musisz ręcznie określić wszystkie nagłówki, które zaakceptujesz (przynajmniej tak było w przypadku mnie w FF 4.0 i Chrome 10.0.648.204).

Metoda $ .ajax jQuery wysyła nagłówek „x-request-with” dla wszystkich żądań międzydomenowych (myślę, że jest to jedyna międzydomenowa).

Tak więc brakujący nagłówek potrzebny do odpowiedzi na żądanie OPTIONS to:

//no longer needed as of jquery 1.5.2
Access-Control-Allow-Headers: x-requested-with

Jeśli przekazujesz jakieś nie „proste” nagłówki, musisz je uwzględnić na liście (wysyłam jeszcze jeden):

//only need part of this for my custom header
Access-Control-Allow-Headers: x-requested-with, x-requested-by

Podsumowując, oto moje PHP:

// * wont work in FF w/ Allow-Credentials
//if you dont need Allow-Credentials, * seems to work
header('Access-Control-Allow-Origin: http://www.example.com');
//if you need cookies or login etc
header('Access-Control-Allow-Credentials: true');
if ($this->getRequestMethod() == 'OPTIONS')
{
  header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
  header('Access-Control-Max-Age: 604800');
  //if you need special headers
  header('Access-Control-Allow-Headers: x-requested-with');
  exit(0);
}
Will Mason
źródło
5
Zauważ, że jQuery 1.5.2 zmieniło swoje zachowanie. Nie dodaje już nagłówka „X-Requested-With”, więc może to już nie stanowić problemu. blog.jquery.com/2011/03/31/jquery-152-released (Bug 8423)
Magmatic
1
@TimK, masz rację! Nie zauważyłem, że wypuszczają 1.5.2. To powiedziawszy, działa to również, jeśli potrzebujesz go przed lotem. Zaktualizowałem odpowiedź.
Will Mason
Więc jestem zdezorientowany. Skończyło się na tym, że musiałeś napisać średniozaawansowany skrypt PHP? Więc nie musisz się więc martwić o używanie Ajax, prawda? Albo coś mi brakuje. Czy nie ma rozwiązania obsługującego tylko JavaScript?
Elisabeth
1
@Elisabeth Ta metoda działa tylko wtedy, gdy kontrolujesz żądane miejsce docelowe ... NIE jest to skrypt pośredni. Jest to wierzchołek naszego PHP żądanej lokalizacji. Czy to ma większy sens?
Will Mason
2
Tak! dzięki Will. Myślałem, że możesz kontrolować wszystko od strony klienta, ale wygląda na to, że potrzebujesz kontroli obu końców.
Elisabeth,
18

Inną możliwością jest to, że ustawienie dataType: jsonpowoduje, że JQuery wysyła Content-Type: application/jsonnagłówek. Jest to uważane za niestandardowy nagłówek przez CORS i wymaga żądania wstępnego CORS. Więc kilka rzeczy do wypróbowania:

1) Spróbuj skonfigurować serwer, aby wysyłał prawidłowe odpowiedzi dotyczące inspekcji wstępnej. Będzie to miało postać dodatkowych nagłówków, takich jak Access-Control-Allow-Methodsi Access-Control-Allow-Headers.

2) Upuść dataType: jsonustawienie. JQuery powinien zażądać Content-Type: application/x-www-form-urlencodeddomyślnie, ale po prostu mieć pewność, można zastąpić dataType: jsonzcontentType: 'application/x-www-form-urlencoded'

monsur
źródło
Dzięki za pomysły. Próbowałem nie ustawiać dataType i ustawiać je na application/x-www-form-urlencodedrówne text/plain. Spróbowałem dodać nagłówek odpowiedzi Access-Control-Allow-Methods "POST, GET, OPTIONS"Nic nie działało.
Magmatic
Czy możesz zajrzeć do konsoli błędów JavaScript (lub konsoli Firebuga) i sprawdzić, czy podczas żądania są jakieś błędy? Ponadto, jeśli wiesz, jak korzystać z Wiresharka, możesz go użyć, aby zobaczyć rzeczywiste żądania HTTP przechodzące przez sieć.
monsur
1
„Inną możliwością jest to, że ustawienie dataType: json powoduje, że JQuery wyśle ​​nagłówek Content-Type: application / json” - tak się nie dzieje. dataTypewpływa na Acceptnagłówek żądania, ale nie na Content-Typenagłówek żądania.
Quentin
9

Wysyłasz „parametry” w js: request.send(params);

ale „dane” w jquery ”. Czy dane są zdefiniowane ?: data:data,

Masz również błąd w adresie URL:

$.ajax( {url:url,
         type:"POST",
         dataType:"json",
         data:data, 
         success:function(data, textStatus, jqXHR) {alert("success");},
         error: function(jqXHR, textStatus, errorThrown) {alert("failure");}
});

Miksujesz składnię ze składnią $ .post


Aktualizacja : szukałem w Google na podstawie odpowiedzi monsur i stwierdziłem, że musisz dodać Access-Control-Allow-Headers: Content-Type(poniżej jest pełny akapit)

http://metajack.im/2010/01/19/crossdomain-ajax-for-xmpp-http-binding-made-easy/

Jak działa CORS

CORS działa bardzo podobnie do pliku crossdomain.xml Flasha. Zasadniczo przeglądarka wyśle ​​żądanie między domenami do usługi, ustawiając źródło nagłówka HTTP na serwerze żądającym. Usługa zawiera kilka nagłówków, takich jak Access-Control-Allow-Origin, aby wskazać, czy takie żądanie jest dozwolone.

W przypadku menedżerów połączeń BOSH wystarczy określić, że wszystkie źródła są dozwolone, ustawiając wartość Access-Control-Allow-Origin na *. Nagłówek Content-Type musi również znajdować się na białej liście w nagłówku Access-Control-Allow-Headers.

Wreszcie, w przypadku niektórych typów żądań, w tym żądań menedżera połączeń BOSH, kontrola uprawnień będzie przeprowadzana przed wysłaniem. Przeglądarka wykona żądanie OPTIONS i oczekuje z powrotem niektórych nagłówków HTTP, które wskazują, które źródła są dozwolone, które metody są dozwolone i jak długo będzie trwała autoryzacja. Na przykład, oto co łatki Punjab i ejabberd, które zwróciłem dla OPCJI:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type 
Access-Control-Max-Age: 86400
Aleadam
źródło
1
Przepraszam. Tak. var data = {action:"something"}
Magmatic
Możesz porównać składnię obu funkcji tutaj: api.jquery.com/jQuery.post
Aleadam
Właśnie wypróbowałem to z adresem URL w ustawieniach, ale ten sam problem. Funkcja .ajax może działać w obie strony.
Magmatic
Miałem już dwa takie nagłówki. Dodałem pozostałe dwa. Wciąż „niepowodzenie” z jQuery. Zwykły javascript nadal działa.
Magmatic
Ostatnią rzeczą, o której przychodzi mi do głowy, jest użycie api.jquery.com/jQuery.ajaxSetup do ustawiania jQuery.ajaxSetup({'beforeSend': function(xhr) {xhr.setRequestHeader(string, string)}})i grania z różnymi wysłanymi nagłówkami (przykład dla rails tutaj: railscasts.com/episodes/136-jquery )
Aleadam
1

Cors zmieniają metodę żądania, zanim to zrobisz, z POST na OPCJE, więc Twoje dane pocztowe nie zostaną wysłane. Sposób, w jaki działał, aby rozwiązać ten problem z procesorami, to wykonanie żądania za pomocą ajax, które nie obsługuje metody OPTIONS. przykładowy kod:

        $.ajax({
            type: "POST",
            crossdomain: true,
            url: "http://localhost:1415/anything",
            dataType: "json",
            data: JSON.stringify({
                anydata1: "any1",
                anydata2: "any2",
            }),
            success: function (result) {
                console.log(result)
            },
            error: function (xhr, status, err) {
                console.error(xhr, status, err);
            }
        });

z tymi nagłówkami na serwerze C #:

                    if (request.HttpMethod == "OPTIONS")
                    {
                          response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With");
                          response.AddHeader("Access-Control-Allow-Methods", "GET, POST");
                          response.AddHeader("Access-Control-Max-Age", "1728000");
                    }
                    response.AppendHeader("Access-Control-Allow-Origin", "*");
Lucas silva de souza sandim
źródło
-2

Zmodyfikuj swoje Jquery w następujący sposób:

$.ajax({
            url: someurl,
            contentType: 'application/json',
            data: JSONObject,
            headers: { 'Access-Control-Allow-Origin': '*' }, //add this line
            dataType: 'json',
            type: 'POST',                
            success: function (Data) {....}
});
Soma Sarkar
źródło
Dlaczego miałbym chcieć, aby moje callas Ajax były synchroniczne !?
Radko Dinev
contentType: 'application/json', data: JSONObject,- Serwer nie oczekuje JSON, więc wysyłanie JSON nie miałoby sensu. Również nie ma czegoś takiego jak obiekt JSON .
Quentin
1
headers: { 'Access-Control-Allow-Origin': '*' }, //add this line- Nigdy tego nie rób. Access-Control-Allow-Originjest nagłówkiem odpowiedzi , a nie nagłówkiem żądania. W najlepszym przypadku nic to nie da. W najgorszym przypadku przekształci żądanie z prostego żądania na wstępnie sprawdzone żądanie, co sprawia, że ​​staje się trudniejsze do obsłużenia na serwerze.
Quentin