【问题标题】:CORS issue on Google Cloud Platform using Python, Flask & Angular使用 Python、Flask 和 Angular 在 Google Cloud Platform 上的 CORS 问题
【发布时间】:2020-07-12 17:16:23
【问题描述】:

我正在 GCP(谷歌云平台)上使用 Python、Flask 和 Angular 开发单页 Web 应用程序,并且遇到了与 CORS 相关的顽固问题。当我构建和服务前端时,我收到以下错误:

从源“https://mySPA-devshell.appspot.com”访问“https://jsonData-devshell.appspot.com/org”处的 XMLHttpRequest 已被 CORS 策略阻止:请求的资源上不存在“Access-Control-Allow-Origin”标头。

后台

为了提供 CORS,我已经导入了 flask_cors 模块,并通过在我的服务器文件 main.py 中添加 CORS(app) 将其添加到服务器文件中。

main.py

from .entities.entity import Session, eco_env_engine, Base
from .entities.org import Org, OrgSchema
from flask import Flask, jsonify
from flask_cors import CORS


app = Flask(__name__)
# enable cross origin resource sharing
CORS(app)

Base.metadata.create_all(eco_env_engine)

@app.route('/org', methods=['GET'])
def get_org():
    session = Session()
    org_objects = session.query(Org).all()
    # transform into JSON-serializable objects
    schema = OrgSchema(many=True)
    org = schema.dump(org_objects)
    # serialize as json
    session.close()
    response = jsonify(org)
    return response

我有一个 shell 脚本 server.sh,它创建了一个我可以在 http://0.0.0.0:5000 访问的接口。

server.sh


# set ../src/main.py as the value of the FLASK_APP environment variable
export FLASK_APP=../src/main.py
# FLASK_ENV=development
# FLASK_DEBUG=1

#activate the virtual environment
source $(pipenv --venv)/bin/activate

#run flask listening on all interfaces
#-h binds to interface
flask run -h 0.0.0.0

当我使用 curl -I http://0.0.0.0:5000/org 我回来了

HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 4766
Access-Control-Allow-Origin: *
Server: Werkzeug/1.0.0 Python/3.7.3
Date: Wed, 01 Apr 2020 01:27:11 GMT

注意 Access-Control-Allow-Origin:* 标头,据我了解,该标头应该解决问题,但事实并非如此。

我正在 GCP Cloud Shell 编辑器中编写我的代码,并从 GCP Cloud Shell 终端中进行部署。为了让事情运行起来,我使用./server.sh & 部署我的后端,使用ng serve 部署我的前端

我尝试过的 CORS 变体:

CORS(app, resource={r'*/*': {'origins': '*/*'}})
CORS(app, resources={r"*": {"origins": ['https://mySPA-devshell.appspot.com', 'https://jsonData-devshell.appspot.com']}})
CORS(app, resources={r'*': {"origins": "*"}})
CORS(app, resources={r"*": {"origins": ['http://localhost:4200', 'http://localhost:5000']}})

前端

org-api.service.ts

import {Injectable} from '@angular/core';
import {HttpClient, HttpErrorResponse} from '@angular/common/http';
import {HttpHeaders, HttpClientModule} from '@angular/common/http';
import {Observable} from 'rxjs/Observable';
import {API_URL} from '../env';
import {Org} from './org.model';
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';

@Injectable()
export class OrgApiService {

  //create private variable 'http' of type HttpClient
  constructor(private http: HttpClient) {
  }

  private static _handleError(err: HttpErrorResponse | any) {
    // return Observable.throw(err.message || 'Error: Unable to complete request.');
    return throwError(err.message || 'Error: Unable to complete request.');
  }

  getOrg(): Observable<Org[]> {
    return this.http    
      .get<Org[]>(`${API_URL}/org`)
      .pipe(catchError(OrgApiService._handleError));
  }

}

org.component.ts

import {Component, OnDestroy, OnInit} from '@angular/core';
import {Subscription} from 'rxjs/Subscription';
import {Org} from './org.model';
import {OrgApiService} from './org-api.service';

@Component({
  selector: 'org',
   template: `
     <div>
       <ul>
         <li *ngFor="let org of orgList">
           {{org.name}}
         </li>
       </ul>
     </div>
   `,
  styleUrls: ['org.component.css'],
})

export class OrgComponent implements OnInit, OnDestroy {
  orgListSubs: Subscription;
  orgList: Org[];
  authenticated = false;

  constructor(private orgApi: OrgApiService) { }

  ngOnInit() {
    this.orgListSubs = this.orgApi
      .getOrg()
      .subscribe(res => {
          this.orgList = res;
        },
        console.error
      );
    const self = this;
  }

  ngOnDestroy() {
    this.orgListSubs.unsubscribe();
  }
}

此外,我遇到了以下解决方案并决定使用它,因为为什么不...无济于事: 在我的前端目录中创建一个proxy.conf.json,内容如下

{
    "/": {
        "target": "http://0.0.0.0:5000",
        "secure": false,
        "logLevel": "debug"
    }
}

然后将proxyConfig 选项添加到serve 目标。

更新

@sideshowbarker 指出在某些情况下 302 响应没有“Access-Control-Allow-Origin”标头,这在我的情况下是正确的。 在进一步挖掘后,我遇到了this 帖子,其中提到 chrome 在收到状态代码 302 时取消了请求。它还让我进入了 chrome://net-export/ ,在那里我发现我的请求也被取消了。

对此我还没有明显/公认的解决方案。如果您正在阅读本文并有建议..请分享。

t=131761 [st=  4]      QUIC_CONNECTION_MIGRATION_MODE
                       --> connection_migration_mode = 0
t=131761 [st=  4]     +HTTP_TRANSACTION_SEND_REQUEST  [dt=0]
t=131761 [st=  4]        HTTP_TRANSACTION_QUIC_SEND_REQUEST_HEADERS
                         --> :method: GET
                             :authority: 5000-dot-XXXXXXX-dot-devshell.appspot.com
                             :scheme: https
                             :path: /
                             accept: application/json, text/plain, */*
                             sec-fetch-dest: empty
                             user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36
                             origin: https://4200-dot-XXXXXXX-dot-devshell.appspot.com
                             sec-fetch-site: cross-site
                             sec-fetch-mode: cors
                             referer: https://4200-dot-XXXXXXX-dot-devshell.appspot.com/?authuser=0&environment_name=default
                             accept-encoding: gzip, deflate, br
                             accept-language: en-US,en;q=0.9
                         --> quic_priority = 4
                         --> quic_stream_id = 5
t=131761 [st=  4]     -HTTP_TRANSACTION_SEND_REQUEST
t=131761 [st=  4]     +HTTP_TRANSACTION_READ_HEADERS  [dt=168]
t=131928 [st=171]        HTTP_TRANSACTION_READ_RESPONSE_HEADERS
                         --> HTTP/1.1 302
                             status: 302
                             date: Wed, 01 Apr 2020 06:33:46 GMT
                             content-type: text/html; charset=utf-8
                             content-length: 498
                             location: https://some.url.com
                             via: 1.1 google
                             alt-svc: quic=":443"; ma=2592000; v="46,43",h3-Q050=":443"; ma=2592000,h3-Q049=":443"; ma=2592000,h3-Q048=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,h3-T050=":443"; ma=2592000
t=131929 [st=172]     -HTTP_TRANSACTION_READ_HEADERS
t=131929 [st=172]      HTTP_CACHE_WRITE_INFO  [dt=0]
t=131929 [st=172]      HTTP_CACHE_WRITE_DATA  [dt=0]
t=131929 [st=172]      HTTP_CACHE_WRITE_INFO  [dt=0]
t=131929 [st=172]      NETWORK_DELEGATE_HEADERS_RECEIVED  [dt=1]
t=131930 [st=173]      URL_REQUEST_DELEGATE_RECEIVED_REDIRECT  [dt=0]
t=131930 [st=173]      CANCELLED
t=131930 [st=173] -REQUEST_ALIVE

我花了几天时间试图解决这个问题,但是我基本上没有成功。我觉得好像有一个比我尝试过的所有方法更简单的解决方案。提前感谢您的帮助!

【问题讨论】:

  • 响应 Headers.status: 302 。它提供了指向包含我要查找的数据的位置的链接。 @sideshowbarker
  • 看来问题可能在于 302 响应没有 Access-Control-Allow-Origin 标头。默认情况下,某些(大多数)服务器不会向 3xx 重定向添加额外的标头。您是否尝试在 302 响应中将 URL 放入 Location 标头的值中,并更改代码以直接向该 URL 发出请求? (也许您现有的请求 URL 和该 Location 标头中的 URL 之间的区别只是一个 / 尾部斜杠。)
  • @sideshowbarker 我应该通过将get&lt;Org[]&gt;(`${API_URL}/org`) 中的${API_URL}/org 替换为位置标头网址来拨打org-api.service.ts 的电话吗?
  • @sideshowbarker 如上所述,我调用了位置 url。作为回应,我收到了状态:400。我调用的位置 url 似乎是 2FA 的结果;这是一个非常长的网址。当我将此网址粘贴到浏览器中时,它会更改为jsonData-devshell.appspot.com/org。然后我尝试调用jsonData-devshell.appspot.com/org,如上一条评论中所述,并获得状态 302
  • https://jsondata-devshell.appspot.com/org 服务器不是你控制的吗?您没有对该服务器环境的管理员访问权限?

标签: python angular google-cloud-platform cors flask-cors


【解决方案1】:

解决了!

将前端/应用程序中的env.ts 编辑为

export const API_URL = 'http://localhost:4200'

而不是localhost:5000

API_URL 对应 ng serve 开发部署

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-03-25
    • 1970-01-01
    • 2020-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-09
    • 2015-04-12
    相关资源
    最近更新 更多