Javascript 写入Google电子表格API时CORS预飞行失败
我正在开发一个使用谷歌电子表格的JS应用程序。我使用OAuth授权通过REST接口访问它们,当我坚持Javascript 写入Google电子表格API时CORS预飞行失败,javascript,jquery,cors,google-spreadsheet-api,Javascript,Jquery,Cors,Google Spreadsheet Api,我正在开发一个使用谷歌电子表格的JS应用程序。我使用OAuth授权通过REST接口访问它们,当我坚持GET用于阅读的请求时,一切都很好 我想使用显示的API添加一个新的工作表。这需要一个POST请求,其内容类型非常奇怪:application/atom+xml,我喜欢这样(JQuery): 这使得Chrome根据CORS要求发出飞行前请求。飞行前选项请求失败-谷歌没有在响应中包含访问控制允许源站标题,Chrome拒绝前进: OPTIONS https://spreadsheets.google.
GET
用于阅读的请求时,一切都很好
我想使用显示的API添加一个新的工作表。这需要一个POST
请求,其内容类型非常奇怪:application/atom+xml,我喜欢这样(JQuery):
这使得Chrome根据CORS要求发出飞行前请求。飞行前选项
请求失败-谷歌没有在响应中包含访问控制允许源站
标题,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.
但是,如果我直接对同一个URL进行GET
(这实际上是您阅读电子表格的方式),我会do获取Access Control-*
标题,这意味着打算支持CORS。与标准内容类型(如text/plain
)的POST
请求相同,这些请求不会触发飞行前选项
——我会获取CORS标题(即使请求无法处理错误的内容类型)
有没有人知道如何解决这个问题,或者通过浏览器“正确”地解决这个问题?或者,在浏览器JS中,指向能够对Google电子表格执行“写入”操作的任何工作代码的指针也很好
如果可能的话,我只想将此应用程序保留在客户端—我知道,使用处理Google API交互的服务器端组件,这件事会更容易。
我知道这不是你问题的答案,但我只是找到了我自己问题的答案。我已经从更新了我的ajax调用
$.ajax({
url: 'https://spreadsheets.google.com/feeds/worksheets/{0}/private/full',
headers: {
'GData-Version': '3.0',
'Authorization': 'Bearer ' + authToken
}
});
到
删除了GData版本标题
首字母
我也有同样的问题。我想这是最近几天出现的一个问题,因为对我来说,这是上周开始工作的一个习惯。我还没有尝试过在我的项目上写工作表,但在阅读工作表提要时,我被完全相同的错误所困扰。我通过向url添加?回调参数解决了这个问题。这是完全奇怪的,我不明白为什么它会起作用,但这似乎是铬的特殊性 我看到了另一个建议在全局范围中定义回调函数callbackFunction的解决方案,该函数只返回true。我仔细研究了一下,发现?callback=调用一个函数,必须在全局(窗口)范围内,但不必一直定义到顶部,在ajax调用之前将其分配给window.callbackFunc就可以了,它会将文本响应传递给回调。因此:
window.callbackContinue = function (response) {
console.log(response);
}
$.ajax('https://my.url?callback=callbackContinue', ajaxOptions);
完全有效。当我将回调参数添加到URL时,我的CORS问题就消失了。所以现在我使用window.callbackinglobalscopewithaverylongname,因为我在我的应用程序中使用了不同的tajaxcalls,而不是$.ajax({success:…})选项,但这可能只是ajax通道中的生活
我不明白为什么它能工作,我不知道它是否能与写操作一起工作,或者它是否特定于工作表提要。我在读取电子表格提要时没有遇到此错误,因此有可疑之处。我现在做的是一个辅助项目,我不用编织,所以将来有一天我会尝试写操作,看看会发生什么。现在我只想提出这个公认的奇怪想法。不管我上面写了什么。这只能解决部分问题。谷歌表单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');
所以现在我使用的不是$.ajax
,而是window.corsAway.ajax
,结果完全相同。服务器端代理设计用于从远程服务器返回数据,或将收到的任何HTML错误传递回ajax
写一个名为CorsAway的实用程序似乎有点不对劲,但是嘿。服务器端代理检查域并只将内容传递给已批准的域(目前仅限谷歌),那么会出现什么问题,对吗?有人告诉我是否会出问题。:-) 我现在使用服务器端代理来代表我的JS执行请求,并在代理中处理预飞行。到目前为止,我还没有找到其他解决办法或任何线索来解释为什么会发生这种情况。我也非常希望这种方法能够奏效。我不知道为什么我们为get而不是POST操作得到正确的CORS头。。。非常令人沮丧。
window.callbackContinue = function (response) {
console.log(response);
}
$.ajax('https://my.url?callback=callbackContinue', ajaxOptions);
// #### #### ##### #### ## ## ## ## ## ##
// ## ## ## ## ## ## ## #### ## ## #### ## ##
// ## ## ## ##### #### ## ## ## # ## ## ## ####
// ## ## ## ## ## ## ## ###### ####### ###### ##
// #### #### ## ## #### ## ## ## ## ## ## ##
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');