Skip to content

Handler before_request doesn't work as expected. #5513

Closed

Description

@hrimov

I want to implement CORS processing without any third party libraries (like flask-cors). My desire is simple: assign multiple headers to any response, access and process the pre-flight OPTIONS request.

class CORSMiddleware:
    def before_request(self):
        if request.method == "OPTIONS":
            response = jsonify({"status": "ok"})
            return self.process_response(response)

    def after_request(self, response):
        return self.process_response(response)

    # noinspection PyMethodMayBeStatic
    def process_response(self, response):
        if response:
            response.headers.add("Access-Control-Allow-Origin", "*")
            response.headers.add("Access-Control-Allow-Headers", "Content-Type, Authorization")
            response.headers.add("Access-Control-Allow-Methods", "GET, POST, OPTIONS, DELETE, PUT")
        return response

    def register(self, app: Flask):
        app.before_request(self.before_request)
        app.after_request(self.after_request)

It can be tested like this

# Note: helper function to reduce view logic
def add_cors_headers(response):
    response.headers.add("Access-Control-Allow-Origin", "*")
    response.headers.add("Access-Control-Allow-Headers", "Content-Type,Authorization")
    response.headers.add("Access-Control-Allow-Methods", "GET,POST,OPTIONS,DELETE,PUT")
    return response


user_blueprint = Blueprint("user", __name__, url_prefix="/users")


@user_blueprint.route("/signup", methods=["POST", "OPTIONS"])
def create_user_handler():
    # Note: uncomment me to see a desired behaviour
    # if request.method == "OPTIONS":
    #     response = jsonify({"status": "ok"})
    #     return add_cors_headers(response)
    # response = jsonify({"message": "registered"})
    # return add_cors_headers(response)
    return {"message": "registered"}


def create_app() -> Flask:
    app = Flask(__name__)
    app.register_blueprint(user_blueprint)
    # Note: comment me to see a desired behaviour
    CORSMiddleware().register(app)
    return app


if __name__ == "__main__":
    app = create_app()
    app.run(host="127.0.0.1", port=5000, debug=True

With the middleware, it handles a preflight OPTIONS request as an original one:
Screenshot 2024-07-01 at 10 59 27

But the desired behaviour should be like that (you can comment middleware registration and uncomment logic in the view-handler):
Screenshot 2024-07-01 at 11 00 15

Also, I was able to implement desired functionality with the middleware, but with the Werkzeug one:

from werkzeug.wrappers import Request, Response


class CORSMiddleware:
    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):
        request_ = Request(environ)

        if request_.method == "OPTIONS":
            response = Response(status=200)
            response = self.process_response(response)
            return response(environ, start_response)

        def custom_start_response(status, headers, exc_info=None):
            response_headers = self.process_headers(headers)
            return start_response(status, response_headers, exc_info)

        return self.app(environ, custom_start_response)

    def process_response(self, response):
        response.headers.add("Access-Control-Allow-Origin", "*")
        response.headers.add("Access-Control-Allow-Headers",
                             "Content-Type, Authorization")
        response.headers.add("Access-Control-Allow-Methods",
                             "GET, POST, OPTIONS, DELETE, PUT")
        return response

    def process_headers(self, headers):
        headers.append(("Access-Control-Allow-Origin", "*"))
        headers.append(("Access-Control-Allow-Headers", "Content-Type, Authorization"))
        headers.append(
            ("Access-Control-Allow-Methods", "GET, POST, OPTIONS, DELETE, PUT"))
        return headers


# and registering it like that
def create_app() -> Flask:
    app = Flask(__name__)
    app.register_blueprint(user_blueprint)
    app.wsgi_app = CORSMiddleware(app.wsgi_app)
    return app

So, my question is: what could be wrong with the Flask middleware?

Activity

  1. locked and limited conversation to collaborators on Jul 1, 2024
  2. converted this issue into a discussionon Jul 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions