Получение ошибки Bad request 400 при попытке отправить XMLHttpRequest на http://api.lbs.yandex.net/geolocation

Мне нужно отправить XMLHttpRequest на сервер Яндекса

Я получаю ошибку Bad Request (400) и:

XMLHttpRequest не может загрузить http://api.lbs.yandex.net/geolocation. В запрошенном ресурсе отсутствует заголовок «Access-Control-Allow-Origin». Таким образом, доступ к источнику 'http://localhost' запрещен. Ответ имел код состояния HTTP 400.

Вот мой код:

var xhr = new XMLHttpRequest();
var url = "http://api.lbs.yandex.net/geolocation";

xhr.open("POST", url, true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

xhr.onreadystatechange = function () {
  <some code>
};

var data = JSON.stringify({
 "common": {
   "version": "1.0",
   "api_key": <here goes API key>
   },
   "gsm_cells": [
   {
   "countrycode": params[0],
   "operatorid": params[1],
   "cellid": params[3],
   "lac": params[2],
   "age": 0
   }
   ]
});

xhr.send(data);

Я не могу найти решение для такой простой (?) вещи!


person Maria Chibel    schedule 24.08.2017    source источник


Ответы (2)


Этот API ожидает данные в формате application/x-www-form-urlencoded, которые выглядят следующим образом:

json={"common":{"version":"1.0","api_key":…}…}

То есть ему нужна пара key=value со строкой json в качестве ключа и неким JSON в качестве значения.

Но код в вопросе отправляет некоторый JSON без необходимого предшествующего ему json=. Таким образом, конечная точка API отвечает кодом 400, чтобы сообщить вам, что это неверный запрос.

Таким образом, вы можете исправить это и получить ответ, заставив свой код сделать это:

var data = 'json=' + JSON.stringify({…});

Однако даже после того, как вы это сделаете, ваш браузер по-прежнему не позволит вашему внешнему JavaScript получить доступ к ответу, возвращаемому сервером. Вместо этого браузер будет регистрировать сообщение об ошибке, подобное этому:

Не удалось загрузить https://api.lbs.yandex.net/geolocation: нет доступа Заголовок -Control-Allow-Origin присутствует на запрошенном ресурсе. Таким образом, доступ к источнику https://foo.bar запрещен.

…потому что протокол CORS требует, чтобы браузеры запрещали доступ внешнего кода JavaScript к ответам на запросы из разных источников, если ответ не имеет Access-Control-Allow-Origin.

Но вы можете обойти это, отправив запрос через прокси-сервер CORS; полный пример:

var xhr = new XMLHttpRequest();
var proxyurl = "https://cors-anywhere.herokuapp.com/";
var url = "https://api.lbs.yandex.net/geolocation";

xhr.open("POST", proxyurl + url, true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

xhr.onreadystatechange = function() {
  console.log(xhr.responseText)
};

var data = 'json=' + JSON.stringify({
  "common": {
    "version": "1.0",
    "api_key": "AAwkGkwBAAAA9muWLAMAKp9XjTBZtmOLeiBQJqHX6YEqNdUAAAAAAAAAAAAoEP1ZsBlcVFA_OpP55MK3Ek1r8A=="
  },
  "gsm_cells": [{
    "countrycode": 250,
    "operatorid": 99,
    "cellid": 42332,
    "lac": 36002,
    "age": 0
  }]
});
xhr.send(data);

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

Так что вам лучше настроить свой собственный прокси, используя https://github.com/Rob--W/cors-anywhere/


Что касается того, как работает прокси в приведенном выше примере: Префикс https://cors-anywhere.herokuapp.com/ на URL-адрес вашего запроса приводит к тому, что запрос выполняется через этот прокси-сервер, который затем:

  1. Перенаправляет запрос на любой https://api.lbs.yandex.net/geolocation.
  2. Получает ответ от https://api.lbs.yandex.net/geolocation.
  3. Добавляет в ответ заголовок Access-Control-Allow-Origin.
  4. Передает этот ответ с этим добавленным заголовком обратно в ваш запрашивающий интерфейсный код.

Затем браузер разрешит вашему коду внешнего интерфейса получить доступ к ответу, потому что этот ответ с заголовком ответа Access-Control-Allow-Origin — это то, что видит браузер.


См. также https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

person sideshowbarker    schedule 24.08.2017
comment
Спасибо за быстрый и очень хорошо объясненный ответ! Вы были правы, добавление json = ... к запросу данных исправляет первую ошибку. Хотя я думаю, что должно быть другое решение, кроме прокси, для решения второй проблемы, потому что yandex.geolocation является общедоступным сервисом и должен работать с запросами с разных серверов. - person Maria Chibel; 26.08.2017
comment
Да, это работает из любого небраузерного инструмента или среды выполнения бэкэнд-программирования, но если заставить его работать для запросов, сделанных непосредственно из внешнего кода JavaScript, работающего в браузере, я гарантирую вам, что определенно нет никакого другого способа заставить его работать, кроме как с помощью прокси-сервер CORS, как описано в ответе. В противном случае браузеры заблокируют ваш код от получения ответа, потому что браузеры по умолчанию блокируют весь доступ к ответам на запросы из разных источников, и единственный способ сделать так, чтобы они не блокировались, — это получить для них Access-Control-Allow-Origin. заголовок в ответе - person sideshowbarker; 26.08.2017

Предлагаемое решение не будет работать правильно в общем случае, но правильно работает для ячейки gcm. Это связано с тем, что служба yandex.net/geolocation использовала для определения местоположения пользователя на основе сети Wi-Fi, соты gcm или IP-адреса.

При необходимости используйте только способ на основе IP, тогда требуется конфигурация CORS на вашем веб-сервере.

person JMS    schedule 10.03.2018