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

我一直在尝试在谷歌应用程序引擎上启用CORS头文件,但是我在互联网上find的方法都不适用于我。

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

2017年1月发行说明说

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

在这里可以看到

而启用CORS的解决scheme是将下面的代码片段添加到服务的OpenAPIconfiguration中。

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

所以我遵循这个例子,并在我的代码库中创build了两个文件

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期望这些字段 – 电子邮件,密码1,密码2,其中密码1应与密码2相同。 现在当我发送正确的参数,我得到HTTP 502说

请求的资源上没有“Access-Control-Allow-Origin”标题。 Origin {origin-url}因此不被允许访问。 该响应具有HTTP状态码502

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

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

* 27462上游提前closures连接,同时读取响应头

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

我把头撞了好几天之后,才发现真正的问题。 我的数据库服务器拒绝任何连接到webapp服务器。

由于HTTP 200响应的情况下,Web应用程序应该进行数据库调用,Web应用程序试图连接到数据库服务器。 这个连接花费的时间太长,一旦超过了NGINX的超时时间,NGINX就会用状态码502作为回应。

由于在web应用程序中设置了“访问控制允许来源”标题,因此NGINX没有在响应中设置该标题。 因此,浏览器被解释为CORS拒绝。

只要将我的Web应用程序实例的数据库服务器的IP地址列入白名单,事情就会顺利进行

概要:

  1. 在GAE灵活环境中,不需要openapi.yml文件来为Django应用程序启用CORS
  2. 不要错过检查NGINX日志:

更新:

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

像这样配置数据库:

 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。

如果您不确定云端连接字符串是什么,请转至Google云端平台仪表板,然后选择“存储”部分下的“SQL”选项卡。 您应该会看到一个带有实例连接名称列的表。 该列下的值是您的cloudsql-connection-string。

Nginx作为你的反向代理,因此,作为你的服务器的网关,应该是谁管理CORS对客户端浏览器的请求,作为从超越到你的系统的第一次接触。 不应该是任何后端服务器(既不是你的数据库,也不是)。

在这里你得到了我的默认配置来启用nginx中的CORS,从Ajax调用到我自己的(backserver glassfish)的REST服务。 随时检查和使用它,并希望它为您服务。

 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; } }