在谷歌应用引擎上为Django应用启用CORS

8
我一直在尝试在Google应用引擎上启用CORS头,但是我在互联网上找到的方法都没有起作用。
我的应用程序使用Python / Django编写,我希望我的前端应用程序(单独托管)能够对我的后端平台进行API调用,该平台位于Google应用引擎上。
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"
    }
 ],
...

我会很乐意为您翻译。这段话的意思是:“所以我按照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

现在,应用程序已经成功部署,但它的行为很奇怪。所有应该返回200响应的请求仍然会抛出CORS错误,但返回400状态的请求可以正常工作。
例如-注册API需要这些字段-email、password1、password2,其中password1应该与password2相同。现在当我发送正确的参数时,我得到HTTP 502的响应,说:
没有'Access-Control-Allow-Origin'头在请求的资源上出现。因此,来自{origin-url}的访问不被允许。响应具有HTTP状态代码502。
但是当我发送password1与password2不相同时,我会收到HTTP 400响应,我确信这是从我的代码中发出的,因为如果password1和password2不匹配,响应是代码中编写的字典。此外,在这种情况下,标头具有Access-Control-Allow-Origin为*,但在前一种情况下并非如此。
我还检查了我的nginx错误日志,它显示:
27462个上游在读取响应标头时过早地关闭了连接。
我在这里做错了什么? 这是在GAE中启用CORS的正确方法吗?
2个回答

4

经过苦思冥想几天后,我终于找到了真正的问题。我的数据库服务器拒绝连接到Web应用程序服务器。

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

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

当我将我的Web应用程序实例的IP地址列入数据库服务器的白名单后,事情开始平稳运行。

总结:

  1. 在GAE灵活环境中启用Django应用程序的CORS无需openapi.yml文件。
  2. 不要忘记检查NGINX日志:p

更新:

只是想更新我的答案,以指定通过该方式,您不必将实例的IP添加到SQL实例的白名单IP中

像这样配置DATABASES:

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

请注意数据库中的HOST密钥。 GAE有一种方法,通过该方法您不必将实例的IP列入白名单,但要使其有效,主机应为 cloudsql-connection-string 而不是SQL实例的IP。
如果您不确定cloudsql-connection-string是什么,请转到Google云平台仪表板,然后在存储部分下选择SQL选项卡。 您应该会看到一个带有实例连接名称列的表格。 该列下面的值就是您的cloudsql-connection-string。

1
作为您的反向代理,Nginx应该管理客户端浏览器请求的CORS,作为连接系统之外的第一个接触点。不应该是任何后端服务器(无论是数据库还是其他内容)。
以下是我默认的配置文件,可用于启用Nginx中针对我的自有REST服务(backserver glassfish)的Ajax调用的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;          
    }
}

我认为您无法在GAE中编辑nginx配置。虽然我是GAE的新手。如果可以在不定义自定义运行时环境的情况下完成,请指向正确的资源。 - Swapnil
nginx位于/etc/nginx目录下,该目录中有conf.d或sites-enabled的配置文件。我不知道您可以访问服务器的深度,但如果您有ssh或命令行,这已足以更改该配置文件。 - DvTr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接