Skip to content
Advertisement

Flask CORS work only for first request, what’s the bug in my code?

background

  1. There is a JS app serving at 127.0.0.1:8080, which refers some API serving at 127.0.0.1:5000 by a Flask app. [See FlaskCode]

  2. When I open this js app in Chrome, first request work well and the second request ends with CORS problem, [see ChromeDebug1].

  3. Additionally, I found this ‘OPTIONS’ is response as 405 (method not allow) in Flask output, and the output from flask_cors is not same like first request. [see FlaskOut].

  4. I’m a newbee in FE and python, so if it is stupid bug, please let me know.

  5. my env is

MacOs M1 version11.1
Chrome Version 87.0
Python 3.8.2
Flask 2.1.1
Werkzeug 2.1.1

question

  1. It seems that flask_cors works only once in my code, but what’s wrong?

  2. Look at the difference of first req and second req, seems that second reponse for ‘OPTIONS’ do not have headers ("Access-Control-Allow-Origin", "*")?

  3. why firest request not have log like flask_cors.extension:Request

====== second edit =========

Thanks for David’s advice. I used tcp dump to capture networking, [See wireshark]. This OPTION request is standard in my opinion. So, it lead me to question 4.

  1. why flask print "{"examinationOID":"61e8d2248373a7329e12f29b"}OPTIONS /yd/pass-through/get-examination HTTP/1.1" 405 - while request not have body? Maybe printing is a trash object from last request which is not gc correctly, due to long connection and exception handling?

I have only one python file, and run it with python ./demo2.py --log=INFO

appendix

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)

ChromeDebug1

enter image description here

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

enter image description here

Advertisement

Answer

I was getting a similar error: param1=value1&param2=value2GET /css/base.css HTTP/1.1 where the leading params are from a POST request called just before. Setting threaded=False (as @david-k-hess suggested) helped.

The whole story was:

  • Browser submits a form using POST
  • Flask server responds with a web page
  • The web page contains /css/base.css in its head
  • Browser downloads /css/base.css using GET
  • Flask server returns HTTP 405 because it thinks the method is param1=value1&param2=value2GET

Fix

Fix 1 suggested by @david-k-hess: In Flask app.run(), add threaded=False

Fix 2: It also seems that the problem was fixed by upgrading Flask and Werkzeug to 2.1.2

My environment:

  • Python 3.9
  • Flask 2.1.1
  • Werkzeug 2.1.1

Other than that, my venv contains:

  • Jinja2
  • MarkupSafe
  • click
  • colorama
  • importlib-metadata
  • itsdangerous
  • pip
  • setuptools
  • wheel
  • zipp
User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement