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

Swa*_*nil 8 django google-app-engine nginx cors

我一直在尝试在Google应用引擎上启用CORS标头,但我在互联网上找到的方法都不适合我.

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

2017年1月发行说明说明了这一点

我们正在改变可扩展服务代理(ESP)的行为以默认拒绝跨源资源共享(CORS)请求

这里可以看到

并且启用它们提供的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"
    }
 ],
...
Run Code Online (Sandbox Code Playgroud)

所以我按照这个例子在我的代码库中创建了两个文件

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
Run Code Online (Sandbox Code Playgroud)

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"
Run Code Online (Sandbox Code Playgroud)

然后我运行了这个命令:

gcloud service-management deploy openapi.yml
Run Code Online (Sandbox Code Playgroud)

然后我编辑了我的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>"
Run Code Online (Sandbox Code Playgroud)

然后我刚刚部署了应用程序

gcloud app deploy
Run Code Online (Sandbox Code Playgroud)

现在,该应用程序已成功部署,但它表现得很奇怪.应该返回200响应的所有请求仍然会抛出CORS错误,但返回400状态的请求确实有效.

例如 - 注册API需要这些字段 - email,password1,password2,其中password1应与password2相同.现在,当我发送正确的参数时,我得到HTTP 502说

请求的资源上不存在"Access-Control-Allow-Origin"标头.因此,不允许原始{origin-url}访问.响应具有HTTP状态代码502

但是当我发送密码1与密码2不同时,我得到HTTP 400响应,我确信它来自我的代码,因为如果密码1和密码2不匹配,则响应是代码中写的字典.同样在这种情况下,标题具有Access-Control-Allow-Origin*,但在前一种情况下,这不是真的

我还检查了我的nginx错误日志

*27462上游过早关闭连接,同时读取响应头

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

Swa*_*nil 4

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

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

由于“access-control-allow-origin”标头是从 web 应用程序设置的,NGINX 并未在其响应中设置该标头。因此,浏览器将其解释为 CORS 拒绝。

一旦我将数据库服务器的 Web 应用程序实例的 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>
}
Run Code Online (Sandbox Code Playgroud)

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

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