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

时间:2021-05-17 23:16:16

I have been trying to enable CORS headers on Google app engine but none of the methods that I found over the internet worked for me.

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

My application is on Python/Django and I want my frontend application (which is hosted separately) to be able to make API calls to my backend platform on Google App Engine.

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

The January 2017 release notes say that

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

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

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

It can be seenhere

可以看到它

And the solution to enable CORS given by them is to add the following snippet to the service's OpenAPI configuration.

并且启用它们提供的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"
    }
 ],
...

So I followed this example and created two files in my code base

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

openapi.yml :

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:

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"

Then I ran this command:

然后我运行了这个命令:

gcloud service-management deploy openapi.yml

Then I edited my app.yml file to make it look like this (The addition was endpoints_api_service. Before adding this, the app was getting deployed without any errors):

然后我编辑了我的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>"

Then I just deployed the application with

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

gcloud app deploy

Now, the app got deployed successfully but it is behaving strangely. All the requests which are supposed to return a 200 response still throw CORS error but the ones which return a 400 status do work.

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

For example - The sign up API expects these fields - email, password1, password2 where password1 should be same as password2. Now when I send correct parameters, I get HTTP 502 saying

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

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

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

But when I send password1 not same as password2, I get HTTP 400 response which I am sure is coming from my code because the response is a dictionary written in the code if password1 and password2 do not match. Also in this case, the headers have Access-Control-Allow-Origin as * but in the former case, that was not true

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

I also checked my nginx error logs and it says

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

*27462 upstream prematurely closed connection while reading response header

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

What am I doing wrong here? Is this the right way to enable CORS in GAE?

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

2 个解决方案

#1


4  

After banging my head for several days, I was able to figure out the the real problem. My database server was denying any connection to the webapp server.

在敲了几天之后,我能够找出真正的问题。我的数据库服务器拒绝与webapp服务器的任何连接。

Since in case of a HTTP 200 response, the webapp is supposed to make a database call, the webapp was trying to connect to the database server. This connection was taking too long and as soon as it reached beyond the NGINX's timeout time, NGINX used to send a response to the web browser with the status code as 502.

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

Since the 'access-control-allow-origin' header was being set from the webapp, NGINX did not set that header in its response. Hence the browser was interpreting it as a CORS denial.

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

As soon as I whitelisted my webapp's instance's IP address for the database server, things started running smoothly

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

Summary:

概要:

  1. There is no need of openapi.yml file to enable CORS for a Django application on GAE flexible environment
  2. 在GAE灵活环境中,不需要openapi.yml文件来为Django应用程序启用CORS
  3. Do not miss to check the NGINX logs :p
  4. 不要错过检查NGINX日志:p

Update:

更新:

Just wanted to update my answer to specify the way through which you won't have to add you instance's IP to the whitelisted IP(s) of the SQL instance

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

Configure the DATABASES like this:

像这样配置DATABASES:

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

Note the HOST key in the databases. GAE has a way through which you won't have to whitelist your instance's IP but for that to work, the host should be the cloudsql-connection-string and NOT the IP of the SQL instance.

请注意数据库中的HOST密钥。 GAE有一种方法可以让您不必将实例的IP列入白名单,但为了使其工作,主机应该是cloudsql-connection-string而不是SQL实例的IP。

If you are not sure what's your cloudsql-connection-string, go to the Google cloud platform dashboard and select the SQL tab under the Storage section. You should see a table with a column Instance connection name. The value under this column is your cloudsql-connection-string.

如果您不确定您的cloudsql-connection-string是什么,请转到Google云平台信息中心,然后选择“存储”部分下的“SQL”选项卡。您应该看到一个包含列实例连接名称的表。此列下的值是您的cloudsql-connection-string。

#2


1  

Nginx as your reverse proxy, so, as the gateway to your server, should be who manage CORS against client browser requests, as first contact from beyond to your system. Should not be any of the backend servers (neither your database, neither anything).

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

Here you got my default config to enable CORS in nginx from Ajax calls to a REST service of my own (backserver glassfish). Feel free to check and use it and hope it serves to you.

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

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

#1


4  

After banging my head for several days, I was able to figure out the the real problem. My database server was denying any connection to the webapp server.

在敲了几天之后,我能够找出真正的问题。我的数据库服务器拒绝与webapp服务器的任何连接。

Since in case of a HTTP 200 response, the webapp is supposed to make a database call, the webapp was trying to connect to the database server. This connection was taking too long and as soon as it reached beyond the NGINX's timeout time, NGINX used to send a response to the web browser with the status code as 502.

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

Since the 'access-control-allow-origin' header was being set from the webapp, NGINX did not set that header in its response. Hence the browser was interpreting it as a CORS denial.

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

As soon as I whitelisted my webapp's instance's IP address for the database server, things started running smoothly

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

Summary:

概要:

  1. There is no need of openapi.yml file to enable CORS for a Django application on GAE flexible environment
  2. 在GAE灵活环境中,不需要openapi.yml文件来为Django应用程序启用CORS
  3. Do not miss to check the NGINX logs :p
  4. 不要错过检查NGINX日志:p

Update:

更新:

Just wanted to update my answer to specify the way through which you won't have to add you instance's IP to the whitelisted IP(s) of the SQL instance

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

Configure the DATABASES like this:

像这样配置DATABASES:

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

Note the HOST key in the databases. GAE has a way through which you won't have to whitelist your instance's IP but for that to work, the host should be the cloudsql-connection-string and NOT the IP of the SQL instance.

请注意数据库中的HOST密钥。 GAE有一种方法可以让您不必将实例的IP列入白名单,但为了使其工作,主机应该是cloudsql-connection-string而不是SQL实例的IP。

If you are not sure what's your cloudsql-connection-string, go to the Google cloud platform dashboard and select the SQL tab under the Storage section. You should see a table with a column Instance connection name. The value under this column is your cloudsql-connection-string.

如果您不确定您的cloudsql-connection-string是什么,请转到Google云平台信息中心,然后选择“存储”部分下的“SQL”选项卡。您应该看到一个包含列实例连接名称的表。此列下的值是您的cloudsql-connection-string。

#2


1  

Nginx as your reverse proxy, so, as the gateway to your server, should be who manage CORS against client browser requests, as first contact from beyond to your system. Should not be any of the backend servers (neither your database, neither anything).

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

Here you got my default config to enable CORS in nginx from Ajax calls to a REST service of my own (backserver glassfish). Feel free to check and use it and hope it serves to you.

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

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