【问题标题】:Getting the Bad request error 400 trying to send XMLHttpRequest to http://api.lbs.yandex.net/geolocation尝试将 XMLHttpRequest 发送到 http://api.lbs.yandex.net/geolocation 时出现错误请求错误 400
【发布时间】:2018-02-01 17:52:58
【问题描述】:

我需要发送 XMLHttpRequest 到 Yandex 服务器

我收到错误请求错误 (400) 并且:

XMLHttpRequest 无法加载 http://api.lbs.yandex.net/geolocation。不 请求中存在“Access-Control-Allow-Origin”标头 资源。因此,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);

对于这么简单(?)的事情,我找不到解决方案!

【问题讨论】:

    标签: javascript cors geolocation xmlhttprequest yandex


    【解决方案1】:

    该 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:请求的资源上不存在“Access-Control-Allow-Origin”标头。 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/设置你自己的代理


    就上面示例中的代理的工作方式而言:在您的请求 URL 前添加 https://cors-anywhere.herokuapp.com/ 前缀会导致请求通过该代理发出,然后:

    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

    【讨论】:

    • 感谢您提供快速且解释清楚的答案!你是对的,在数据​​查询中添加“json = ...”可以修复第一个错误。虽然我认为必须有另一种解决方案,但代理来解决第二个问题,因为 yandex.geolocation 是一项公共服务,必须处理来自不同服务器的请求。
    • 是的,它适用于任何非浏览器工具或后端编程运行时,但就使其适用于直接从浏览器中运行的前端 JavaScript 代码发出的请求而言,我向您保证绝对没有其他的除了以答案中描述的方式使用CORS代理之外,使其工作的方法。否则浏览器会阻止您的代码获取响应,因为默认情况下浏览器会阻止对来自跨域请求的响应的所有访问,而使它们不被阻止的唯一方法是让它们获得 Access-Control-Allow-Origin响应中的标头
    【解决方案2】:

    建议的 sulution 在一般情况下无法正常工作,但对 gcm 电池正常工作。这是因为 yandex.net/geolocation 服务用于根据 WiFi 网络、gcm 单元或 IP 地址获取用户的位置。

    当需要仅使用基于 IP 的方式时,则需要在您的 Web 服务器上配置 CORS。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-10
      • 1970-01-01
      • 2016-01-06
      • 1970-01-01
      • 2020-03-30
      相关资源
      最近更新 更多