【发布时间】:2016-06-18 18:07:08
【问题描述】:
我有一个 Google App Engine 应用程序,当一切都在一台主机上运行时,它在生产环境中运行良好,而当网络应用程序在另一台主机上运行时,它主要运行良好。所有发往/来自服务器的查询(GET、POST、PUT、DELETE)都按预期运行。这向我表明,我在整个系统中正确配置了所有 CORS(我在几周前进行了那场战斗并且一切都解决了)。
我唯一不能做的是文件上传。我正在使用django、djangoappengine、django-cors-headers 和filetransfers,最终结果是从远程服务器运行时我无法上传文件,但其他一切工作正常。在 Chrome 的 JavaScript 控制台中,我看到以下错误:
XMLHttpRequest cannot load http://localhost:8080/_ah/upload/ahl...<truncated>.
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:9000' is therefore not allowed
access. The response had HTTP status code 405.
这显然是一个 CORS 错误,所以我大致知道需要发生什么。除了我不知道如何对我的配置进行必要的更改来克服这个问题。
这是我的整体设置:
-
dev_appserver.py为 8080 端口上的 API 提供服务 -
grunt serve在端口 9000 上为客户端应用程序提供服务 - CORS 设置:
- 开发:
CORS_ORIGIN_ALLOW_ALL = True - 制作:
CORS_ORIGIN_WHITELIST = [ '(app.domain.com for my app)' ]
- 开发:
在生产中,我相信修复将是configure CORS on my bucket,但我并不肯定。但是,我不知道如何为此配置本地开发服务器,以便在部署之前测试整体数据流。
以下是最终失败的 JavaScript(该应用正在使用 AngularJS):
var form = angular.element('#media-form');
var data = new FormData(form);
// have the API return a URL using prepare_upload in
// filetransfers module to upload to:
var uploadActionUrl = https://api.domain.com/upload_url/';
$http.get(uploadActionUrl)
.then(function(response) {
// I get here with no problem
$http.post(response.data.action, formData)
.then(function(response) {
console.log('got:', response);
}, function(error) {
console.log('upload error:', error); // <- this is where I end up
});
}, function(error) {
console.log('get upload URL error:', error);
});
同样,当从同一主机运行时,非常类似于此的代码可以正常运行(因此 API 本身可以正常运行),并且(重要的是)所有 HTTP 方法都可以在所有端点上工作,而不是上传我的文件,因此 CORS 本身已设置正确设置为与 App Engine 交互。只有文件上传部分不起作用。
我突然想到,修复可能包括使用 JSON 而不是 FormData 组装我的上传表单,但我过去从未找到这样做的方法。
--- 更新到添加 ---
澄清一点,导致此错误的端点并不直接在我的应用程序中,它位于由单独的 Google 服务处理的 URL 上。给我 URL 的代码是:
from google.appengine.ext.blobstore import create_upload_url
def prepare_upload(request, url, **kwargs):
return create_upload_url(
url,
gs_bucket_name = settings.GOOGLE_CLOUD_STORAGE_BUCKET
), {}
我返回的 URL 格式为 /_ah/upload/<one-time key>,在该 URL 上发生的一切(似乎)都超出了我的控制范围,包括添加标题。
【问题讨论】:
-
当我说“这在从同一主机运行时有效”时,我应该澄清一下。由于之前的应用程序没有使用 AngularJS,因此发布和响应处理的细节略有不同;有可能我在这里也做错了其他事情,但在解决 CORS 问题之前我无法解决任何问题。
-
url的处理程序是否发送 CORS 标头?下面是一个例子,说明你需要做什么才能让它工作:github.com/GoogleCloudPlatform/… -
为什么不直接将formData中的文件POST到端点,而不是先get再post呢?喜欢
var input = $('selector'); var data = new FormData(); data.append('name', input.name); data.append('type', input.type); data.append(input.name, input.files[0]); $.ajax({ url: 'url', type: 'POST', processData: false, contentType: false, // important for jqXHR data: data}.done() -
端点 URL 由数据存储即时创建,并且只能使用一次,因此我必须从服务器(GET)检索 URL,然后使用 POST 到该端点要上传的文件。
-
我从来没有找到一个好的解决方案。相反,我在 AppEngine 项目上创建了一个表单,它有几个 JavaScript 挂钩来指示重要事件(已加载、选择文件、上传完成等),然后我将该表单作为 IFrame 嵌入到客户端应用程序中并监听这些事件.它很丑,但它可以完成工作。
标签: google-app-engine xmlhttprequest cors djangoappengine django-cors-headers