【问题标题】:CORS preflight fails when writing to Google Spreadsheet API写入 Google 电子表格 API 时,CORS 预检失败
【发布时间】:2013-12-08 19:22:16
【问题描述】:

我正在开发一个使用 Google 电子表格的 JS 应用程序。我使用 OAuth 授权通过 REST 接口访问它们,当我坚持 GET 用于阅读的请求时,一切都很好。

我想使用in the docs 显示的 API 添加一个新工作表。这需要带有相当奇怪的Content-type: application/atom+xmlPOST 请求,我喜欢这样(JQuery):

$.ajax("https://spreadsheets.google.com/feeds/worksheets/{{key}}/private/full", {
  type: "POST",
  contentType: "application/atom+xml",
  headers: { Authorization: "Bearer" + token },
  data: data
});

由于 CORS 要求,这会使 Chrome 发出预检请求。预检 OPTIONS 请求失败 - Google 未在响应中包含 Access-Control-Allow-Origin 标头,Chrome 拒绝继续:

OPTIONS https://spreadsheets.google.com/feeds/.../private/full 
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.

但是,如果我直接将 GET 指向同一个 URL(这实际上是您阅读电子表格的方式),我确实会得到 Access-Control-* 标头,这意味着 CORS 支持是故意的。与标准内容类型的 POST 请求相同(如 text/plain),不会触发预检 OPTIONS - 我得到了 CORS 标头(即使请求失败对错误的内容类型也是如此)。

有没有人知道如何解决这个问题,或者从浏览器中解决这个问题的“正确”方法?或者,指向能够从浏览器内 JS 对 Google 电子表格执行“写入”操作的任何工作代码的指针也很棒。

我希望仅在可能的情况下将此应用程序保留在客户端 - 我知道使用处理 Google API 交互的服务器端组件,这会更容易。

【问题讨论】:

  • 我也非常希望它能够正常工作。我不知道为什么我们会为 GET 而不是 POST 操作获得正确的 CORS 标头......非常令人沮丧。

标签: javascript jquery cors google-spreadsheet-api


【解决方案1】:

别在意我上面写的。这只解决了部分时间。似乎 Google Sheets API 根本不支持 CORS。我编写了一个服务器端代理,它只将请求传递到 google.com,这是唯一的前进方向。

我想我会分享我的 js 代码,因为我写了一个很好的小东西,可以像 $.ajax 一样使用。也很高兴分享服务器端代码,但您可以使用类似的东西与您自己的服务器端代理进行交互。它不漂亮,但它正在工作。哦,嗯,​​LGPL。这是js:

//  ####    ####   #####    ####     ##    ##   ##    ##   ##  ##
// ##  ##  ##  ##  ##  ##  ##       ####   ##   ##   ####  ##  ##
// ##      ##  ##  #####    ####   ##  ##  ## # ##  ##  ##  ####
// ##  ##  ##  ##  ## ##       ##  ######  #######  ######   ##
//  ####    ####   ##  ##   ####   ##  ##   ## ##   ##  ##   ##

function CorsAway(serverSideUrl) {
    // Server-side proxy handling of cross-domain AJAX requests.
    this.serverSideUrl = serverSideUrl;

    // This hash contains information as to whether each $.ajax parameter should be submitted to $.ajax directly, or passed to the CorsAway server.
    // true means that the parameter should be passed to the CorsAway server
    this.parameterIsForRemoteServer = {
//      accepts:        // not supported
//      async:          // not supported
        beforeSend:     false,              // submit to $.ajax
//      cache:          // not supported, see $.ajax documentation for how to implement
        complete:       false,              // submit to $.ajax
        contents:       false,              // submit to $.ajax
        contentType:    true,               // submit to remote server
        context:        false,              // submit to $.ajax
        converters:     false,              // submit to $.ajax
//      crossDomain:    // not supported
        data:           true,               // submit to remote server
        dataFilter:     false,              // submit to $.ajax
        dataType:       false,              // submit to $.ajax
        error:          false,              // submit to $.ajax
//      global:         // not supported
        headers:        true,               // submit to remote server
//      ifModified:     // not supported
//      isLocal:        // not supported
//      jsonp:          // not supported
//      jsonpCallback:  // not supported
        method:         true,               // submit to remote server
///     mimeType:       true,               // submit to remote server
///     password:       true,               // submit to remote server
//      processData:    // REQUIRES SPECIAL HANDLING: SEE COMMENTS IN CODE BELOW
//      scriptCharset:  // not supported
        statusCode:     false,              // submit to $.ajax
        success:        false,              // submit to $.ajax
        timeout:        false,              // submit to $.ajax
//      traditional:    // not supported
//      type:           // not supported
///     url:            true,               // submit to remote server
///     username:       true                // submit to remote server
//      xhr:            // not supported
//      xhrFields:      // not supported
    }

    // Use it just like $.ajax
    this.ajax = function (url, jqAjaxInfo) {
        //Redirect all requests to a call to the server

        // Sort jqAjaxInfo into parameters for $.ajax and for the remote server
        var localAjaxParams = {};
        var remoteHttpRequestParams = {};
        for(var k in jqAjaxInfo) {
            if(this.parameterIsForRemoteServer[k]) {
                // Submit it to the remote server
                remoteHttpRequestParams[k] = jqAjaxInfo[k];
            } else {        // some parameters are not supported; their behavior is undefined and doesn't matter
                // Submit it to $.ajax
                localAjaxParams[k] = jqAjaxInfo[k];
            }
        }

        // Prepare specially encapsulated data parameter for local $.ajax to submit to server-side CorsAway
        localAjaxParams.data = {
            dataToSubmit:               localAjaxParams.data,
            remoteHttpRequestParams:    remoteHttpRequestParams,
            remoteUrl:                  url
        };
        localAjaxParams.method = 'PUT'; // Always make request to CorsAway by PUT

        // Make call to $.ajax and pass info to server-side CorsAway service
        $.ajax(this.serverSideUrl, localAjaxParams);
    }
}

// Instantiate global object with URL of server-side CorsAway service
window.corsAway = new CorsAway('/local/url/of/corsaway.php');

所以现在我使用window.corsAway.ajax 而不是$.ajax,结果完全相同。服务器端代理旨在从远程服务器返回数据,或者将它收到的任何 HTML 错误传递回 ajax。

编写一个名为 CorsAway 的实用程序似乎有些错误,但是,嘿。服务器端代理检查域并仅将内容传递到已批准的域(现在只有 Google),所以会出现什么问题,对吧?有人告诉我是否会出错。 :-)

【讨论】:

    【解决方案2】:

    更新

    我知道这不是您问题的答案,但我只是自己找到了问题的答案。我已经从

    更新了我的 ajax 调用
    $.ajax({
        url: 'https://spreadsheets.google.com/feeds/worksheets/{0}/private/full',
        headers: {
            'GData-Version': '3.0',
            'Authorization': 'Bearer ' + authToken
        }
    });
    

    $.ajax({
        url: 'https://spreadsheets.google.com/feeds/worksheets/{0}/private/full',
        headers: {
            'Authorization': 'Bearer ' + authToken
        }
    });
    

    删除了 GData-Version 标头。

    初始

    我也有同样的问题。我认为这是最近几天引入的一个问题,因为对我来说这是上周使用的。

    【讨论】:

    • 我现在正在使用服务器端代理来代表我的 JS 执行请求,并在代理中处理预检。到目前为止,我还没有发现其他解决方法或任何关于为什么会发生这种情况的线索。
    【解决方案3】:

    我还没有尝试在我的项目中写入表格,但我一直被从工作表提要中读取的完全相同的错误所困扰。我通过向 url 添加一个 ?callback 参数来解决它。这很奇怪,我不明白它为什么会起作用,但这似乎是 Chrome 的特殊性。

    我看到了另一个解决方案,它建议在全局范围内定义一个回调函数 callbackFunction,它只返回 true。我玩了一下,发现 ?callback= 调用一个函数,必须在全局(窗口)范围内,但不必一直定义在顶部,将它分配给 window.callbackFunc 之前就可以了ajax 调用,并将文本响应传递给回调。所以:

    window.callbackContinue = function (response) {
         console.log(response);
    }
    
    $.ajax('https://my.url?callback=callbackContinue', ajaxOptions);
    

    完全有效。当我将该回调参数添加到 URL 时,我的 CORS 问题就消失了。所以现在我使用 window.callbackInGlobalScopeWithAVeryLongNameBecauseIMakeALotOfDifferentAjaxCallsInMyApplication 而不是 $.ajax({success: ...}) 选项,但这可能只是 ajax 通道中的生活。

    我不明白它为什么起作用,也不知道它是否适用于写操作,或者它是否特定于工作表提要。我没有从电子表格提要中读取此错误,所以有些事情很可疑。我正在做的是一种我做的副项目,而不是编织,所以将来有一天我会尝试写操作,我们会看看会发生什么。只是想暂时提出这个公认的奇怪想法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-30
      • 1970-01-01
      • 1970-01-01
      • 2019-04-18
      • 2017-06-20
      相关资源
      最近更新 更多