在 Google App Engine 上为 Django 应用程序启用 CORS

Enabling CORS on Google App Engine for a Django Application

我一直在尝试在 Google 应用引擎上启用 CORS headers,但我在互联网上找到的 none 方法对我有用。

我的应用程序在 Python/Django 上,我希望我的前端应用程序(单独托管)能够在 Google App Engine 上对我的后端平台进行 API 调用。

2017 年 1 月的发行说明说

We are changing the behavior of the Extensible Service Proxy (ESP) to deny cross-origin resource sharing (CORS) requests by default

可见here

他们给出的启用 CORS 的解决方案是将以下代码片段添加到服务的 OpenAPI 配置中。

"host": "echo-api.endpoints.YOUR_PROJECT_ID.cloud.goog",
"x-google-endpoints": [
    {
      "name": "echo-api.endpoints.YOUR_PROJECT_ID.cloud.goog",
      "allowCors": "true"
    }
 ],
...

所以我按照 this 示例并在我的代码库中创建了两个文件

openapi.yml :

swagger: "2.0"
info:
  description: "Google Cloud Endpoints APIs"
  title: "APIs"
  version: "1.0.0"
host: "echo-api.endpoints.<PROJECT-ID>.cloud.goog"  
x-google-endpoints:
 - name: "echo-api.endpoints.<PROJECT-ID>.cloud.goog"
   allowCors: "true"
paths:
  "/api/v1/sign-up":
    post:
      description: "Sends an email for verfication"
      operationId: "signup"
      produces:
      - "application/json"
      responses:
        200:
          description: "OK"
      parameters:
      - description: "Email address of the user"
        in: body
        name: email
        required: true
        schema:
          type: string
      - description: "password1"
        in: body
        name: password1
        required: true
        schema:
          type: string
      - description: "password2"
        in: body
        name: password2
        required: true
        schema:
          type: string

openapi-appengine.yml:

swagger: "2.0"
info:
  description: "Google Cloud Endpoints API fo localinsights backend server"
  title: "Localinsights APIs"
  version: "1.0.0"
host: "<PROJECT-ID>.appspot.com"

然后我运行这个命令:

gcloud service-management deploy openapi.yml

然后我编辑了我的 app.yml 文件,使它看起来像这样(添加的是 endpoints_api_service。在添加这个之前,应用程序正在部署,没有任何错误):

runtime: python
env: flex
entrypoint: gunicorn -b :$PORT myapp.wsgi

beta_settings:
    cloud_sql_instances: <cloud instance>

runtime_config: 
  python_version: 3

automatic_scaling:
  min_num_instances: 1
  max_num_instances: 1

resources:
  cpu: 1
  memory_gb: 0.90
  disk_size_gb: 10  

env_variables:
  DJANGO_SETTINGS_MODULE: myapp.settings.staging
  DATABASE_URL: <dj-database-url>

endpoints_api_service:
  name: "<PROJECT-ID>.appspot.com"
  config_id: "<CONFIG-ID>"

然后我用

部署了应用程序
gcloud app deploy

现在,该应用程序已成功部署,但它的表现很糟糕运行。所有应该 return 200 响应的请求仍然会抛出 CORS 错误,但是那些 return 400 状态的请求确实有效。

例如 - 注册 API 需要这些字段 - 电子邮件、密码 1、密码 2,其中密码 1 应与密码 2 相同。现在,当我发送正确的参数时,我得到 HTTP 502 saying

No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin {origin-url} is therefore not allowed access. The response had HTTP status code 502

但是,当我发送与密码 2 不同的密码 1 时,我收到了 HTTP 400 响应,我确信它来自我的代码,因为如果密码 1 和密码 2 不匹配,响应是代码中编写的字典。同样在这种情况下,headers 将 Access-Control-Allow-Origin 作为 * 但在前一种情况下,情况并非如此

我还检查了我的 nginx 错误日志,它说

*27462 upstream prematurely closed connection while reading response header

我在这里做错了什么? 这是在 GAE 中启用 CORS 的正确方法吗?

经过几天的摸索,我终于找到了真正的问题所在。我的数据库服务器拒绝与 webapp 服务器的任何连接。

因为在 HTTP 200 响应的情况下,webapp 应该进行数据库调用,webapp 正在尝试连接到数据库服务器。此连接花费的时间太长,一旦超过 NGINX 的超时时间,NGINX 就会向 Web 浏览器发送响应,状态代码为 502。

由于 'access-control-allow-origin' header 是从 webapp 设置的,NGINX 没有在其响应中设置 header。因此浏览器将其解释为 CORS 拒绝。

一旦我将我的 webapp 实例的数据库服务器 IP 地址列入白名单,事情就开始 运行 顺利

总结:

  1. 不需要 openapi.yml 文件来为 GAE 柔性环境中的 Django 应用程序启用 CORS
  2. 不要错过查看 NGINX 日志 :p

更新:

只是想更新我的答案以指定您不必将实例的 IP 添加到 SQL 实例

的白名单 IP 的方式

像这样配置数据库:

DATABASES = {
    'HOST': <your-cloudsql-connection-string>, # This is the tricky part
    'ENGINE': <db-backend>,
    'NAME': <db-name>,
    'USER': <user>,
    'PASSWORD': <password>
}

注意数据库中的主机键。 GAE 有一种方法,您不必将实例的 IP 列入白名单,但要使其正常工作,主机应该是 cloudsql-connection-string 而不是 SQL实例。

如果您不确定您的 cloudsql-connection-string 是什么,请转到 Google 云平台仪表板和 select 存储部分下的 SQL 选项卡。您应该会看到一个 table,其中有一列 实例连接名称 。此列下的值是您的 cloudsql-connection-string.

Nginx 作为您的反向代理,因此,作为您服务器的网关,应该根据客户端浏览器请求管理 CORS,作为从外部到您系统的第一个联系人。不应该是任何后端服务器(既不是你的数据库,也不是任何东西)。

这里你得到了我的默认配置,通过 Ajax 调用我自己的 REST 服务(backserver glassfish)在 nginx 中启用 CORS。欢迎查看和使用,希望对您有所帮助。

server {
    listen   80; ## listen for ipv4; this line is default and implied
    server_name codevault;

    #Glassfish
    location /GameFactoryService/   {

            index index.html;

                add_header Access-Control-Allow-Origin $http_origin;

                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-NginX-Proxy true;
                proxy_ssl_session_reuse off;
                proxy_set_header Host $http_host;
                proxy_redirect off;
                proxy_set_header X-Forwarded-Server $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_pass http://127.0.0.1:18000/GameFactoryService/;

        }

        #static content
    location / {
        root /usr/share/nginx_static_content;
    }

    error_page 500 501 502 503 504 505 506 507 508 509 510 511 /50x.html;

    #error
        location = /50x.html {

      add_header Access-Control-Allow-Origin $http_origin;          
          internal;          
    }
}