3

我有一个 Google App Engine 应用程序,当一切都在一台主机上运行时,它在生产环境中运行良好,而当网络应用程序在单独的主机上运行时,它主要运行良好。与服务器 ( GET, POST, PUT, DELETE) 之间的所有查询都按预期运行。这向我表明我在整个系统中正确配置了所有 CORS(我在几周前进行了那场战斗并且一切都解决了)。

我唯一不能做的是文件上传。我正在使用django, djangoappengine,django-cors-headersfiletransfers, 一切的最终结果是从远程服务器运行时我无法上传文件,但其他一切工作正常。在 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)' ]

在生产中,我相信解决方法是在我的 bucket 上配置 CORS,但我并不积极。但是,我知道如何为此配置本地开发服务器,以便在部署之前测试整体数据流。

这是最终失败的 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 处。给我网址的代码是:

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 上发生的所有事情(似乎)都超出了我的控制范围,包括添加标题。

4

2 回答 2

1

您的 http 处理程序必须具有将 cors 标头发送到浏览器的 OPTIONS 方法。

例如; Chrome 总是在 PUT 请求检查 CORS 标头之前向同一 URL 发送一个 OPTIONS 请求。如果浏览器无法获得 OPTIONS 请求的响应,则 cors 将失败。

检查此应用引擎 webapp2 示例。

class BaseRestHandler(webapp2.RequestHandler):
    def options(self, *args, **kwargs):
        self.response.headers['Access-Control-Allow-Origin'] = '*'
        self.response.headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept'
        self.response.headers['Access-Control-Allow-Methods'] = 'POST, GET, PUT, DELETE'

https://en.wikipedia.org/wiki/Cross-origin_resource_sharing

于 2016-03-11T13:49:56.010 回答
1

一种方法是为您的特定 url 设置 http 标头app.yaml

例如:

handlers:
- url: /_ah/upload....
  ...
  http_headers:
    Access-Control-Allow-Origin: http://localhost:9000
于 2016-03-04T22:49:03.547 回答