python - 使用 Python、Flask 和 Angular 的 Google Cloud Platform 上的 CORS 问题
问题描述
我正在 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 模块,并通过添加CORS(app)
我的服务器文件 main.py将其添加到服务器文件中。
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”标头,这在我的情况下是正确的。在进一步挖掘后,我发现了这篇文章,其中提到 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
我花了几天时间试图解决这个问题,但是我基本上没有成功。我觉得好像有一个比我尝试过的所有方法更简单的解决方案。预先感谢您的帮助!
解决方案
解决了!
env.ts
在前端/应用程序中编辑为
export const API_URL = 'http://localhost:4200'
代替localhost:5000
API_URL 对应 ng serve 开发部署
推荐阅读
- android - 如果生物特征认证失败,如何显示系统密码选项?
- javascript - 如何在同一台硬件机器上运行的javascript进程和python进程之间创建进程间通信
- django - django 模板不显示 ForeignKey 项目
- php - 如何使用中间件,以便用户在查看页面/文件之前必须填写密码?
- netlogo - 在 Netlogo 中计算两点的黑白距离
- oracle - PLSQL DBMS_SCHEDULER 等待响应
- tinymce - TinyMCE file_browser_callback 不显示浏览按钮
- ansible - Ansible:从变量中获取组成员
- c# - PInvoke 在返回结构时仅适用于 64 位
- swift - 使用 Swift 呈现下一个 ViewController 以关闭当前的 ViewController