Flask CORS 仅适用于第一个请求,我的代码中有什么错误?
Flask CORS work only for first request, what's the bug in my code?
背景
有一个 JS 应用服务于 127.0.0.1:8080,它指的是一些 API 服务于 127.0.0.1:5000 的 Flask 应用。 [参见 FlaskCode]
当我在 Chrome 中打开此 js 应用程序时,第一个请求运行良好,第二个请求以 CORS 问题结束,[参见 ChromeDebug1]。
此外,我发现这个'OPTIONS'在Flask输出中是405(方法不允许)的响应,flask_cors的输出与第一个请求不同。 [参见 FlaskOut]。
我是FE新手python,如果是愚蠢的错误,请告诉我。
我的环境是
MacOs M1 version11.1
Chrome Version 87.0
Python 3.8.2
Flask 2.1.1
Werkzeug 2.1.1
问题
似乎 flask_cors 在我的代码中只工作了一次,但是有什么问题吗?
看看第一个req和第二个req的区别,好像'OPTIONS'的第二个reponse没有headers ("Access-Control-Allow-Origin", "*")
?
为什么第一次请求没有像flask_cors.extension:Request
这样的日志
======第二次编辑=========
感谢大卫的建议。我使用 tcp dump 来捕获网络,[参见 wireshark]。这个 OPTION 请求在我看来是标准的。所以,这让我想到了问题 4。
- 为什么 flask 打印
"{"examinationOID":"61e8d2248373a7329e12f29b"}OPTIONS /yd/pass-through/get-examination HTTP/1.1" 405 -
而请求没有正文?也许打印是来自上次请求的垃圾对象,由于长连接和异常处理而未正确 gc?
我只有一个 python 文件,并且 运行 它与 python ./demo2.py --log=INFO
附录
FlaskCode
# -*- coding: UTF-8 -*-
from flask import Flask
from flask import Response
from flask_cors import CORS, cross_origin
import logging
import json
app = Flask(__name__)
CORS(app, supports_credentials=True)
demoDataPath="xxx"
@app.route("/yd/pass-through/get-examination", methods=['POST'])
@cross_origin()
def getexamination():
logging.getLogger('demo2').info('into getexamination')
response = {}
response["code"]=0
response["message"]="good end"
f = open(demoDataPath+"/rsp4getexamination.json", "r")
response["data"]= json.loads(f.read())
return Response(json.dumps(response), mimetype='application/json', status=200)
@app.route("/yd/pass-through/report-config", methods=['POST'])
@cross_origin()
def getconfig():
logging.getLogger('demo2').info('into getconfig')
response = {}
response["code"]=0
response["message"]="good end"
f = open(demoDataPath+"/rsp4getreportconfig.json", "r")
response["data"]= json.loads(f.read())
return Response(json.dumps(response), mimetype='application/json', status=200)
if __name__ == '__main__':
logging.getLogger('flask_cors').level = logging.DEBUG
logging.getLogger('werkzeug').level = logging.DEBUG
logging.getLogger('demo2').level = logging.DEBUG
app.logger.setLevel(logging.DEBUG)
logging.info("app run")
app.run(debug=True, threaded=True, port=5001)
Chrome调试 1
FlaskOut
DEBUG:flask_cors.core:CORS request received with 'Origin' http://127.0.0.1:8080
DEBUG:flask_cors.core:The request's Origin header matches. Sending CORS headers.
DEBUG:flask_cors.core:Settings CORS headers: MultiDict([('Access-Control-Allow-Origin', 'http://127.0.0.1:8080'), ('Access-Control-Allow-Headers', 'content-type, traceid, withcredentials'), ('Access-Control-Allow-Methods', 'DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT'), ('Vary', 'Origin')])
DEBUG:flask_cors.extension:CORS have been already evaluated, skipping
INFO:werkzeug:127.0.0.1 - - [21/Apr/2022 20:33:36] "OPTIONS /yd/pass-through/report-config HTTP/1.1" 200 -
[2022-04-21 20:33:36,736] INFO in demo2: into getconfig
INFO:demo2:into getconfig
DEBUG:flask_cors.core:CORS request received with 'Origin' http://127.0.0.1:8080
DEBUG:flask_cors.core:The request's Origin header matches. Sending CORS headers.
DEBUG:flask_cors.core:Settings CORS headers: MultiDict([('Access-Control-Allow-Origin', 'http://127.0.0.1:8080'), ('Vary', 'Origin')])
DEBUG:flask_cors.extension:CORS have been already evaluated, skipping
INFO:werkzeug:127.0.0.1 - - [21/Apr/2022 20:33:36] "POST /yd/pass-through/report-config HTTP/1.1" 200 -
DEBUG:flask_cors.extension:Request to '/yd/pass-through/get-examination' matches CORS resource '/*'. Using options: {'origins': ['.*'], 'methods': 'DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT', 'allow_headers': ['.*'], 'expose_headers': None, 'supports_credentials': True, 'max_age': None, 'send_wildcard': False, 'automatic_options': True, 'vary_header': True, 'resources': '/*', 'intercept_exceptions': True, 'always_send': True}
DEBUG:flask_cors.core:CORS request received with 'Origin' http://127.0.0.1:8080
DEBUG:flask_cors.core:The request's Origin header matches. Sending CORS headers.
DEBUG:flask_cors.core:Settings CORS headers: MultiDict([('Access-Control-Allow-Origin', 'http://127.0.0.1:8080'), ('Access-Control-Allow-Credentials', 'true'), ('Vary', 'Origin')])
DEBUG:flask_cors.extension:CORS have been already evaluated, skipping
INFO:werkzeug:127.0.0.1 - - [21/Apr/2022 20:33:36] "{"examinationOID":"61e8d2248373a7329e12f29b"}OPTIONS /yd/pass-through/get-examination HTTP/1.1" 405 -
wireshark
我用另一个“错误”解决了我的问题,如下所示。而且我仍然不知道为什么我的代码不能像 flask_cors 文档所说的那样工作。如果您有更好的解决方案或建议,请告诉我。
@app.after_request
def add_headers(response):
response.headers.add('Content-Type', 'application/json')
response.headers.add('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS')
response.headers.add('Access-Control-Allow-Headers', 'content-type, traceid, withcredentials')
response.status=200
logging.getLogger('demo2').info('into add_headers')
return response
问题似乎出在这里:
INFO:werkzeug:127.0.0.1 - - [21/Apr/2022 20:33:36] "{"examinationOID":"61e8d2248373a7329e12f29b"}OPTIONS /yd/pass-through/get-examination HTTP/1.1" 405 -
比较一下:
INFO:werkzeug:127.0.0.1 - - [21/Apr/2022 20:33:36] "OPTIONS /yd/pass-through/report-config HTTP/1.1" 200 -
它正在尝试使用无效的 HTTP 方法 {"examinationOID":"61e8d2248373a7329e12f29b"}OPTIONS
而不是普通的 OPTIONS
。
似乎有什么东西正在破坏您的 CORS headers 并使 Chrome 迷惑不解。一个可能的来源是 Flask-CORS 配置。其他地方是否有一些您没有向我们展示的设置?还是只是使用默认配置?
如果它只是默认值,那么查看 Chrome 中的 Javascript 代码 运行 会有所帮助 - 这可能导致从第一个请求到第二个请求的方法损坏.
此外,在您的 chrome 开发工具屏幕截图中,您正在过滤 'XHR' - 不会显示 OPTIONS 请求,这将帮助您找出问题所在。它们显示在 'Other'.
下
我遇到了类似的错误:
param1=value1¶m2=value2GET /css/base.css HTTP/1.1
其中前导参数来自之前调用的 POST 请求。设置 threaded=False
(如@david-k-hess 所建议的那样)有帮助。
整个故事是:
- 浏览器使用 POST
提交表单
- Flask 服务器响应网页
- 该网页的头部包含
/css/base.css
- 浏览器下载
/css/base.css
使用 GET
- Flask 服务器 returns HTTP 405 因为它认为方法是
param1=value1¶m2=value2GET
修复
@david-k-hess 建议的修复 1:在 Flask app.run()
中,添加 threaded=False
修复 2:似乎通过将 Flask 和 Werkzeug 升级到 2.1.2 也解决了这个问题
我的环境:
- Python 3.9
- 烧瓶 2.1.1
- Werkzeug 2.1.1
除此之外,我的 venv 包含:
- Jinja2
- 标记安全
- 点击
- 彩色
- importlib-metadata
- 很危险
- 点子
- 设置工具
- 车轮
- 齐普
背景
有一个 JS 应用服务于 127.0.0.1:8080,它指的是一些 API 服务于 127.0.0.1:5000 的 Flask 应用。 [参见 FlaskCode]
当我在 Chrome 中打开此 js 应用程序时,第一个请求运行良好,第二个请求以 CORS 问题结束,[参见 ChromeDebug1]。
此外,我发现这个'OPTIONS'在Flask输出中是405(方法不允许)的响应,flask_cors的输出与第一个请求不同。 [参见 FlaskOut]。
我是FE新手python,如果是愚蠢的错误,请告诉我。
我的环境是
MacOs M1 version11.1
Chrome Version 87.0
Python 3.8.2
Flask 2.1.1
Werkzeug 2.1.1
问题
似乎 flask_cors 在我的代码中只工作了一次,但是有什么问题吗?
看看第一个req和第二个req的区别,好像'OPTIONS'的第二个reponse没有headers
("Access-Control-Allow-Origin", "*")
?为什么第一次请求没有像
这样的日志flask_cors.extension:Request
======第二次编辑=========
感谢大卫的建议。我使用 tcp dump 来捕获网络,[参见 wireshark]。这个 OPTION 请求在我看来是标准的。所以,这让我想到了问题 4。
- 为什么 flask 打印
"{"examinationOID":"61e8d2248373a7329e12f29b"}OPTIONS /yd/pass-through/get-examination HTTP/1.1" 405 -
而请求没有正文?也许打印是来自上次请求的垃圾对象,由于长连接和异常处理而未正确 gc?
我只有一个 python 文件,并且 运行 它与 python ./demo2.py --log=INFO
附录
FlaskCode
# -*- coding: UTF-8 -*-
from flask import Flask
from flask import Response
from flask_cors import CORS, cross_origin
import logging
import json
app = Flask(__name__)
CORS(app, supports_credentials=True)
demoDataPath="xxx"
@app.route("/yd/pass-through/get-examination", methods=['POST'])
@cross_origin()
def getexamination():
logging.getLogger('demo2').info('into getexamination')
response = {}
response["code"]=0
response["message"]="good end"
f = open(demoDataPath+"/rsp4getexamination.json", "r")
response["data"]= json.loads(f.read())
return Response(json.dumps(response), mimetype='application/json', status=200)
@app.route("/yd/pass-through/report-config", methods=['POST'])
@cross_origin()
def getconfig():
logging.getLogger('demo2').info('into getconfig')
response = {}
response["code"]=0
response["message"]="good end"
f = open(demoDataPath+"/rsp4getreportconfig.json", "r")
response["data"]= json.loads(f.read())
return Response(json.dumps(response), mimetype='application/json', status=200)
if __name__ == '__main__':
logging.getLogger('flask_cors').level = logging.DEBUG
logging.getLogger('werkzeug').level = logging.DEBUG
logging.getLogger('demo2').level = logging.DEBUG
app.logger.setLevel(logging.DEBUG)
logging.info("app run")
app.run(debug=True, threaded=True, port=5001)
Chrome调试 1
FlaskOut
DEBUG:flask_cors.core:CORS request received with 'Origin' http://127.0.0.1:8080
DEBUG:flask_cors.core:The request's Origin header matches. Sending CORS headers.
DEBUG:flask_cors.core:Settings CORS headers: MultiDict([('Access-Control-Allow-Origin', 'http://127.0.0.1:8080'), ('Access-Control-Allow-Headers', 'content-type, traceid, withcredentials'), ('Access-Control-Allow-Methods', 'DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT'), ('Vary', 'Origin')])
DEBUG:flask_cors.extension:CORS have been already evaluated, skipping
INFO:werkzeug:127.0.0.1 - - [21/Apr/2022 20:33:36] "OPTIONS /yd/pass-through/report-config HTTP/1.1" 200 -
[2022-04-21 20:33:36,736] INFO in demo2: into getconfig
INFO:demo2:into getconfig
DEBUG:flask_cors.core:CORS request received with 'Origin' http://127.0.0.1:8080
DEBUG:flask_cors.core:The request's Origin header matches. Sending CORS headers.
DEBUG:flask_cors.core:Settings CORS headers: MultiDict([('Access-Control-Allow-Origin', 'http://127.0.0.1:8080'), ('Vary', 'Origin')])
DEBUG:flask_cors.extension:CORS have been already evaluated, skipping
INFO:werkzeug:127.0.0.1 - - [21/Apr/2022 20:33:36] "POST /yd/pass-through/report-config HTTP/1.1" 200 -
DEBUG:flask_cors.extension:Request to '/yd/pass-through/get-examination' matches CORS resource '/*'. Using options: {'origins': ['.*'], 'methods': 'DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT', 'allow_headers': ['.*'], 'expose_headers': None, 'supports_credentials': True, 'max_age': None, 'send_wildcard': False, 'automatic_options': True, 'vary_header': True, 'resources': '/*', 'intercept_exceptions': True, 'always_send': True}
DEBUG:flask_cors.core:CORS request received with 'Origin' http://127.0.0.1:8080
DEBUG:flask_cors.core:The request's Origin header matches. Sending CORS headers.
DEBUG:flask_cors.core:Settings CORS headers: MultiDict([('Access-Control-Allow-Origin', 'http://127.0.0.1:8080'), ('Access-Control-Allow-Credentials', 'true'), ('Vary', 'Origin')])
DEBUG:flask_cors.extension:CORS have been already evaluated, skipping
INFO:werkzeug:127.0.0.1 - - [21/Apr/2022 20:33:36] "{"examinationOID":"61e8d2248373a7329e12f29b"}OPTIONS /yd/pass-through/get-examination HTTP/1.1" 405 -
wireshark
我用另一个“错误”解决了我的问题,如下所示。而且我仍然不知道为什么我的代码不能像 flask_cors 文档所说的那样工作。如果您有更好的解决方案或建议,请告诉我。
@app.after_request
def add_headers(response):
response.headers.add('Content-Type', 'application/json')
response.headers.add('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS')
response.headers.add('Access-Control-Allow-Headers', 'content-type, traceid, withcredentials')
response.status=200
logging.getLogger('demo2').info('into add_headers')
return response
问题似乎出在这里:
INFO:werkzeug:127.0.0.1 - - [21/Apr/2022 20:33:36] "{"examinationOID":"61e8d2248373a7329e12f29b"}OPTIONS /yd/pass-through/get-examination HTTP/1.1" 405 -
比较一下:
INFO:werkzeug:127.0.0.1 - - [21/Apr/2022 20:33:36] "OPTIONS /yd/pass-through/report-config HTTP/1.1" 200 -
它正在尝试使用无效的 HTTP 方法 {"examinationOID":"61e8d2248373a7329e12f29b"}OPTIONS
而不是普通的 OPTIONS
。
似乎有什么东西正在破坏您的 CORS headers 并使 Chrome 迷惑不解。一个可能的来源是 Flask-CORS 配置。其他地方是否有一些您没有向我们展示的设置?还是只是使用默认配置?
如果它只是默认值,那么查看 Chrome 中的 Javascript 代码 运行 会有所帮助 - 这可能导致从第一个请求到第二个请求的方法损坏.
此外,在您的 chrome 开发工具屏幕截图中,您正在过滤 'XHR' - 不会显示 OPTIONS 请求,这将帮助您找出问题所在。它们显示在 'Other'.
下我遇到了类似的错误:
param1=value1¶m2=value2GET /css/base.css HTTP/1.1
其中前导参数来自之前调用的 POST 请求。设置 threaded=False
(如@david-k-hess 所建议的那样)有帮助。
整个故事是:
- 浏览器使用 POST 提交表单
- Flask 服务器响应网页
- 该网页的头部包含
/css/base.css
- 浏览器下载
/css/base.css
使用 GET - Flask 服务器 returns HTTP 405 因为它认为方法是
param1=value1¶m2=value2GET
修复
@david-k-hess 建议的修复 1:在 Flask app.run()
中,添加 threaded=False
修复 2:似乎通过将 Flask 和 Werkzeug 升级到 2.1.2 也解决了这个问题
我的环境:
- Python 3.9
- 烧瓶 2.1.1
- Werkzeug 2.1.1
除此之外,我的 venv 包含:
- Jinja2
- 标记安全
- 点击
- 彩色
- importlib-metadata
- 很危险
- 点子
- 设置工具
- 车轮
- 齐普