Запрос CORS POST работает с простым JavaScript, но почему не с jQuery?

88

Я пытаюсь сделать запрос на отправку Cross Origin, и у меня он работает JavaScriptтак:

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

Но хотелось бы использовать jQuery, а заставить работать не получается. Вот что я пытаюсь:

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

Это приводит к отказу. Если кто-нибудь знает, почему jQueryне работает, сообщите нам всем. Спасибо.

(Я использую jQuery1.5.1 и Firefox 4.0, и мой сервер отвечает правильным Access-Control-Allow-Originзаголовком)

Магматический
источник
Это было решение для меня (используйте Javascript XMLHttpRequest), когда я столкнулся с проблемами CORS с Ionic framework 3.
jeudyx

Ответы:

73

ОБНОВЛЕНИЕ: как указал TimK, это больше не нужно с jquery 1.5.2. Но если вы хотите добавить собственные заголовки или разрешить использование учетных данных (имя пользователя, пароль или файлы cookie и т. Д.), Читайте дальше.


Думаю, я нашел ответ! (4 часа и много ругательств спустя)

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

Вам нужно вручную указать все заголовки, которые вы примете (по крайней мере, так было для меня в FF 4.0 и Chrome 10.0.648.204).

Метод jQuery $ .ajax отправляет заголовок «x-requested-with» для всех междоменных запросов (я думаю, это единственный кросс-домен).

Таким образом, отсутствующий заголовок, необходимый для ответа на запрос OPTIONS:

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

Если вы передаете какие-либо «не простые» заголовки, вам нужно будет включить их в свой список (я отправляю еще один):

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

Итак, чтобы собрать все вместе, вот мой 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);
}
Уилл Мейсон
источник
5
Обратите внимание, что jQuery 1.5.2 изменил свое поведение. Он больше не добавляет заголовок «X-Requested-With», так что это больше не проблема. blog.jquery.com/2011/03/31/jquery-152-released (Ошибка 8423)
Magmatic
1
@TimK, ты прав! Я не заметил, что они выпускают 1.5.2. При этом это также работает, если вам нужно, чтобы он был предварительно запущен. Я обновил свой ответ.
Уилл Мейсон
Итак, я запутался. Вам все равно пришлось написать промежуточный PHP-скрипт? Значит, вам не нужно беспокоиться об использовании Ajax, верно? Или я что-то упускаю. Разве нет решения только на JavaScript?
Элизабет
1
@Elisabeth Этот метод работает только в том случае, если вы управляете запрошенным местом назначения ... это НЕ промежуточный сценарий. Это вершина нашего PHP запрошенного местоположения. В этом больше смысла?
Уилл Мейсон
2
Да! спасибо Уилл. Я думал, что вы можете контролировать все со стороны клиента, но похоже, что вам нужно контролировать оба конца.
Элизабет
18

Другая возможность заключается в том, что настройка dataType: jsonзаставляет JQuery отправлять Content-Type: application/jsonзаголовок. CORS считает это нестандартным заголовком и требует предварительного запроса CORS. Итак, несколько вещей, которые стоит попробовать:

1) Попробуйте настроить сервер на отправку правильных предполетных ответов. Это будет в виде дополнительных заголовков, таких как Access-Control-Allow-Methodsи Access-Control-Allow-Headers.

2) Отбросьте dataType: jsonнастройку. JQuery должен запрашивать Content-Type: application/x-www-form-urlencodedпо умолчанию, но на всякий случай вы можете заменить его dataType: jsonнаcontentType: 'application/x-www-form-urlencoded'

монсур
источник
Спасибо за идеи. Я пробовал не устанавливать dataType, а устанавливать его как application/x-www-form-urlencodedи even text/plain. И я попытался добавить заголовок ответа Access-Control-Allow-Methods "POST, GET, OPTIONS"Ничего не сработало.
Magmatic
Можете ли вы заглянуть в консоль ошибок JavaScript (или консоль Firebug) и увидеть, есть ли ошибки во время запроса? Кроме того, если вы знаете, как использовать Wireshark, вы можете использовать это, чтобы увидеть фактические HTTP-запросы, проходящие по сети.
монсур
1
«Другая возможность состоит в том, что установка dataType: json заставляет JQuery отправлять заголовок Content-Type: application / json» - этого не происходит. dataTypeвлияет на Acceptзаголовок запроса, но не на Content-Typeзаголовок запроса.
Quentin
9

Вы отправляете «параметры» в js: request.send(params);

но "данные" в jquery ". Данные определены ?: data:data,

Кроме того, у вас есть ошибка в URL:

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

Вы смешиваете синтаксис с синтаксисом для $ .post


Обновление : я искал в Google на основе ответа monsur, и я обнаружил, что вам нужно добавить Access-Control-Allow-Headers: Content-Type(ниже полный абзац)

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

Как работает CORS

CORS работает очень похоже на файл crossdomain.xml во Flash. По сути, браузер отправляет междоменный запрос службе, устанавливая HTTP-заголовок Origin для запрашивающего сервера. Служба включает несколько заголовков, таких как Access-Control-Allow-Origin, чтобы указать, разрешен ли такой запрос.

Для диспетчеров соединений BOSH достаточно указать, что все источники разрешены, установив для параметра Access-Control-Allow-Origin значение *. Заголовок Content-Type также должен быть внесен в белый список в заголовке Access-Control-Allow-Headers.

Наконец, для определенных типов запросов, включая запросы диспетчера соединений BOSH, проверка разрешений будет выполняться заранее. Браузер выполнит запрос OPTIONS и ожидает получения некоторых заголовков HTTP, которые указывают, какие источники разрешены, какие методы разрешены и как долго будет длиться эта авторизация. Например, вот какие патчи Punjab и ejabberd я вернул для OPTIONS:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type 
Access-Control-Max-Age: 86400
Алеадам
источник
1
Сожалею. Да. var data = {action:"something"}
Magmatic
Вы можете сравнить синтаксис обеих функций здесь: api.jquery.com/jQuery.post
Aleadam
Я просто попробовал с URL-адресом в настройках, но такая же проблема. Функция .ajax может принять это в любом случае.
Magmatic
У меня уже было два таких заголовка. Я добавил два других. По-прежнему «провал» с jQuery. Обычный javascript все еще работает.
Magmatic
Последнее, что я могу придумать, это использовать api.jquery.com/jQuery.ajaxSetup для установки jQuery.ajaxSetup({'beforeSend': function(xhr) {xhr.setRequestHeader(string, string)}})и воспроизведения различных отправленных заголовков (пример для рельсов здесь: railscasts.com/episodes/136-jquery )
Aleadam,
1

Cors изменяет метод запроса, прежде чем он будет выполнен, с POST на OPTIONS, поэтому ваши почтовые данные не будут отправлены. Для решения этой проблемы cors использовался способ выполнения запроса с помощью ajax, который не поддерживает метод OPTIONS. пример кода:

        $.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);
            }
        });

с этими заголовками на сервере С #:

                    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", "*");
Лукас сильва де соуса сандим
источник
-2

Измените свой JQuery следующим образом:

$.ajax({
            url: someurl,
            contentType: 'application/json',
            data: JSONObject,
            headers: { 'Access-Control-Allow-Origin': '*' }, //add this line
            dataType: 'json',
            type: 'POST',                
            success: function (Data) {....}
});
Сома Саркар
источник
Зачем мне делать мои каллы Ajax синхронными !?
Радько Динев
contentType: 'application/json', data: JSONObject,- Сервер не ожидает JSON, поэтому отправка JSON не имеет смысла. Также не существует такой вещи, как объект JSON .
Quentin
1
headers: { 'Access-Control-Allow-Origin': '*' }, //add this line- Никогда не делай этого. Access-Control-Allow-Originэто заголовок ответа , а не заголовок запроса. В лучшем случае это ничего не даст. В худшем случае он преобразует запрос из простого в предварительный, что усложняет работу на сервере.
Quentin