Почему я получаю запрос OPTIONS вместо запроса GET?

289
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js" type="text/javascript"></script>
<script>
$.get("http://example.com/", function(data) {
     alert(data);
});
</script>

он делает запрос OPTIONS к этому URL, а затем обратный вызов никогда не вызывается ни с чем.

Когда это не кросс-домен, он работает нормально.

Разве jQuery не должен просто сделать вызов с <script>узлом, а затем сделать обратный вызов, когда он загружен? Я понимаю, что не смогу получить результат (поскольку он междоменный), но это нормально; Я просто хочу, чтобы звонок прошел. Это ошибка или я что-то не так делаю?

Пол Тарьян
источник
2
Может быть потому, что из кросс-домена. Например, если вы находитесь в своем файле File: // PATH_TO_WEBSITE вместо использования localhost / WEBSITE_LINK
James111

Ответы:

263

Согласно MDN ,

Предварительно оформленные запросы

В отличие от простых запросов (обсужденных выше), «предварительно выданные» запросы сначала отправляют заголовок запроса HTTP OPTIONS ресурсу в другом домене, чтобы определить, является ли фактический запрос безопасным для отправки. Межсайтовые запросы предварительно просматриваются следующим образом, поскольку они могут иметь значение для пользовательских данных. В частности, запрос предварительно просвечивается, если:

  • Он использует методы, отличные от GET или POST. Кроме того, если POST используется для отправки данных запроса с Content-Type, отличным от application / x-www-form-urlencoded, multipart / form-data или text / plain, например, если запрос POST отправляет полезную нагрузку XML на сервер используя application / xml или text / xml, запрос предварительно просвечивается.
  • Он устанавливает пользовательские заголовки в запросе (например, запрос использует заголовок, такой как X-PINGOTHER)
arturgrigor
источник
44
это решило нашу проблему, переход с «application / json» на «text / plain» остановил ужасный запрос опций
Keeno
10
что я не понимаю, так это то, что браузер запрашивает метод OPTIONS, просто чтобы убедиться, что реальный запрос безопасен для отправки. но в каком смысле? я имею в виду, что сервер также может устанавливать ограничения с определенными заголовками ответа, так зачем это нужно?
Hardik
11
@hardik Помните, что добавляя CORS, вы потенциально принимаете запросы от всех, в которых они могут манипулировать данными на вашем сервере с помощью запросов (POST, PUT, DELETE и т. д.). В этих ситуациях, например при использовании пользовательских заголовков, браузер сначала проверяет на сервере, что сервер готов принять запрос, прежде чем отправлять его, так как отправка нежелательных запросов на сервер может быть очень опасной для ваших данных, а также, что точка в браузере, отправляющая потенциально большие полезные нагрузки, если сервер не желает их принимать, следовательно, перед проверкой проверяется OPTIONS.
davidnknight
6
@davidnknight, если отправка данных на сервер может быть опасной, то есть сервер может быть скомпрометирован, тогда, конечно, злонамеренный сервер ответит на ваш запрос OPTIONS с помощью «Конечно, отправьте все заново!». Как это безопасность? (честный вопрос)
Мэтт
3
«Предварительные запросы не относятся к безопасности. Скорее, они не меняют правила». - См. Ответ на вопрос « Что является мотивацией для введения предварительных запросов»
FMJaguar
9

Если вы пытаетесь POST

Обязательно к JSON.stringifyвашей форме данные и отправьте как text/plain.

<form id="my-form" onSubmit="return postMyFormData();">
    <input type="text" name="name" placeholder="Your Name" required>
    <input type="email" name="email" placeholder="Your Email" required>
    <input type="submit" value="Submit My Form">
</form>

function postMyFormData() {

    var formData = $('#my-form').serializeArray();
    formData = formData.reduce(function(obj, item) {
        obj[item.name] = item.value;
        return obj;
    }, {});
    formData = JSON.stringify(formData);

    $.ajax({
        type: "POST",
        url: "https://website.com/path",
        data: formData,
        success: function() { ... },
        dataType: "text",
        contentType : "text/plain"
    });
}
Дерек Соике
источник
2

Я не верю, что jQuery естественным образом выполнит запрос JSONP, когда ему будет предоставлен такой URL Однако он выполнит запрос JSONP, когда вы сообщите ему, какой аргумент использовать для обратного вызова:

$.get("http://metaward.com/import/http://metaward.com/u/ptarjan?jsoncallback=?", function(data) {
     alert(data);
});

Этот аргумент полностью зависит от принимающего скрипта (который не обязательно должен называться «jsoncallback»), поэтому в этом случае функция никогда не будет вызываться. Но, поскольку вы заявили, что просто хотите, чтобы скрипт на metaward.com выполнялся, он бы это сделал.

VoteyDisciple
источник
МОЙ колбэк все еще будет уведомлен, что элемент скрипта полностью загружен? Я просто хочу убедиться, что обновление произошло до того, как я запросил его у API.
Пол Тарьян
Вы получите, если принимающий скрипт поддерживает JSONP и готов вызвать функцию, которую вы идентифицируете. Если скрипт ничего не делает, кроме как генерирует блок данных JSON без какого-либо другого поведения, вы не сможете определить, когда он завершит загрузку. Если важно сообщить, когда он завершит загрузку, вы можете рассмотреть возможность реализации скрипта на своем собственном сервере, который действует как прокси.
VoteyDisciple
1

Фактически, междоменные запросы AJAX (XMLHttp) не разрешены по соображениям безопасности (подумайте о том, чтобы выбрать «ограниченную» веб-страницу со стороны клиента и отправить ее обратно на сервер - это было бы проблемой безопасности).

Единственный обходной путь - обратные вызовы. Это: создание нового объекта сценария и указание src на конечный JavaScript, который является обратным вызовом со значениями JSON (myFunction ({data}), myFunction - это функция, которая что-то делает с данными (например, хранит их) в переменной).

Адриан Наварро
источник
1
верно, но я могу загрузить его в <script src = ""> или <img src = "">, и браузер с удовольствием его ударит. Я просто хочу знать, когда он будет полностью загружен, чтобы я мог запросить результат импорта.
Пол Тарьян
1

Просто измените «application / json» на «text / plain» и не забудьте JSON.stringify (request):

var request = {Company: sapws.dbName, UserName: username, Password: userpass};
    console.log(request);
    $.ajax({
        type: "POST",
        url: this.wsUrl + "/Login",
        contentType: "text/plain",
        data: JSON.stringify(request),

        crossDomain: true,
    });
Дэвид Лопес
источник
1

У меня такая же проблема. Я решил добавить заголовки в мой PHP-скрипт, которые присутствуют только в среде разработки.

Это позволяет междоменные запросы:

header("Access-Control-Allow-Origin: *");

Это сообщает предварительному запросу, что клиент может отправлять любые заголовки, которые он хочет:

header("Access-Control-Allow-Headers: *");

Таким образом, нет необходимости изменять запрос.

Если у вас есть конфиденциальные данные в вашей базе данных разработчиков, которые могут быть утечки, подумайте об этом дважды.

fivebit
источник
1

В моем случае проблема не была связана с CORS, так как я выпускал JQuery POST для того же веб-сервера. Данные были в формате JSON, но я пропустил параметр dataType: 'json'.

У меня не было (и я не добавил) параметра contentType, как показано в ответе Дэвида Лопеса выше.

GarDavis
источник
0

Похоже, что Firefox и Opera (также протестированные на Mac) не любят кросс-доменную архитектуру (но Safari вполне подойдет).

Возможно, вам придется вызвать локальный код на стороне сервера, чтобы свернуть удаленную страницу.

helloandre
источник
0

Я смог исправить это с помощью следующих заголовков

Access-Control-Allow-Origin
Access-Control-Allow-Headers
Access-Control-Allow-Credentials
Access-Control-Allow-Methods

Если вы находитесь на Nodejs, вот код, который вы можете скопировать / вставить.

app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin','*');
  res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
  res.header('Access-Control-Allow-Credentials', true);
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH');
  next();
});
obai
источник