pallets / flask

The Python micro framework for building web applications.

Home Page:https://flask.palletsprojects.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Issue matching route with methods=['OPTIONS'] when similar route appears later using ['GET', 'POST']

daskalou opened this issue · comments

With routing setup like this:

class ApiHandler:

  @route_with('/<regex("(.*)"):path>', methods = ['OPTIONS'])
  def cors(self, path):
    # Send custom CORS headers
    # This should be matched for any request
    return make_response('OK', 200)

  @route_with('/post/<regex("(.*)"):id>', methods = ['GET', 'POST'])
  def post(self, id):
    # This should only be matched on GET / POST for "/post/<id>"
    return make_response('You are at the post', 200)

The cors handler never gets run if an OPTIONS request is made to /post/123 with the above setup.

Instead, strangely, a blank 200 reponse is returned (with no logs besides the single request entry shown in the logs).

If an OPTIONS request is made to any other URL (or if the post handler is commented out), the cors handler is correctly run.

I've tried running this in multiple Flask version all the way from 1.1.4 to 3.0.1, to no avail.

This appears to be a bug in Flask's / Werkzeug's routing.

Anyone encountered something similar or have any suggestions on what I can do to diagnose / fix?

Similar issue happened years ago, but the solution in the closed issue does not work:

#3187

What is route_with? That's not from Flask.

What is the regex() converter type? That's not from Flask. Other people with that type have had problems because its implementation needed to be changed for Werkzeug 3.

I used provide_automatic_options=False and it worked as expected.

from flask import Flask

app = Flask(__name__)

@app.route("/<path:path>", methods=["OPTIONS"])
@app.route("/", methods=["OPTIONS"], defaults={"path": ""})
def handle_options(path: str) -> str:
    return "options"


@app.route("/post/<int:id>", methods=["GET", "POST"], provide_automatic_options=False)
def post(id: int) -> str:
    return f"post {id}"

Thanks for the quick response @davidism .

My apologies, route_with is a decorator which in turn calls blueprint.add_url_rule().

regex() is a custom routing converter, similar to:

https://gist.github.com/ekayxu/5743138

Does the fact we are calling blueprint.add_url_rule() instead of using the @app.route decorator increase the chance that the route is somehow handled differently when matched by Werkzeug?