connexion icon indicating copy to clipboard operation
connexion copied to clipboard

Flask Blueprints before_request not executed for call with bodies

Open jledrumics opened this issue 9 months ago • 0 comments

It seems that FlaskApp Blueprints before_request hooks are not executed for non-GET methods (OK for GET). Flask app before_request hooks are well executed in their side.
It may not be considered as a bug if connexion is not aimed to work with flask hooks, and another mechanisms should be used.

TO REPRODUCE

Python 3.13.2 connexion 3.2.0 Flask 3.1.0

Run the following python file :

import connexion
from flask import Blueprint, jsonify, g
import tempfile

swagger_yaml = """
swagger: "2.0"
info:
  version: "1.0.0"
  title: "Test API"
paths:
  /blueprint/get:
    get:
      operationId: "example.bp_get_route"
      produces:
        - "application/json"
      responses:
        "200":
          description: "OK"
  /blueprint/post:
    post:
      operationId: "example.bp_post_route"
      consumes:
        - "application/json"
      produces:
        - "application/json"
      parameters:
        - in: "body"
          name: "payload"
          required: true
          schema:
            type: object
            properties:
              ifYouWant:
                type: string
      responses:
        "200":
          description: "OK"
  /post:
    post:
      operationId: "example.post_route"
      consumes:
        - "application/json"
      produces:
        - "application/json"
      parameters:
        - in: "body"
          name: "payload"
          required: true
          schema:
            type: object
            properties:
              ifYouWant:
                type: string
      responses:
        "200":
          description: "OK"
"""

with tempfile.NamedTemporaryFile("w", suffix=".yaml", delete=False) as tmp:
    tmp.write(swagger_yaml)
    swagger_file = tmp.name

ticket_bp = Blueprint("test", __name__, url_prefix="/blueprint")


@ticket_bp.before_request
def before_bp_hook():
    g.blueprint_hook_executed = True


@ticket_bp.route("/get")
def bp_get_route():
    bp_flag = g.get("blueprint_hook_executed", False)
    flag = g.get("hook_executed", False)
    return jsonify({"route": "BP GET", "hook_executed": flag, "blueprint_hook_executed": bp_flag})

# ticket_bp.route("/post", methods=["POST"])
@ticket_bp.route("/post")
def bp_post_route():
    bp_flag = g.get("blueprint_hook_executed", False)
    flag = g.get("hook_executed", False)
    is_bug = flag is False or bp_flag is False
    return jsonify({"route": "BP POST", "hook_executed": flag, "blueprint_hook_executed": bp_flag, "is_bug": is_bug})


def post_route():
    flag = g.get("hook_executed", False)
    is_bug = flag is False
    return jsonify({"route": "NORMAL POST", "hook_executed": flag, "is_bug": is_bug})


if __name__ == "__main__":
    app = connexion.FlaskApp(__name__, specification_dir=".")
    app.add_api(swagger_file, pythonic_params=True)
    app.app.register_blueprint(ticket_bp)

    @app.app.before_request
    def before_hook():
        g.hook_executed = True

    app.app.add_url_rule("/post", view_func=post_route)

    app.run(port=5000)

Then make the following calls :

curl -X POST http://localhost:5000/post  -H "Content-Type: application/json" -d '{}'
curl http://localhost:5000/blueprint/get
curl -X POST http://localhost:5000/blueprint/post -H "Content-Type: application/json" -d '{}'

MY WORKAROUND

By defining the method in the app.route decorator, the blueprint hook is executed. i.e @ticket_bp.route("/post", methods=["POST"]).
But I'm not sure whuy it needs to be redefined as it already is defined in swagger.

ADDITIONNAL INFOS

I tried to replicate the issue with flask only code, but it is not possible as Flask required the methods in the route definition (as not coming from a spec).

jledrumics avatar Mar 28 '25 22:03 jledrumics