diff --git a/CHANGES.rst b/CHANGES.rst index fda72fc9d..662bf0ccd 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,11 @@ Version 2.2.1 Unreleased +- Fix router so that ``/path/`` will match a rule ``/path`` if strict + slashes mode is disabled for the rule. :issue:`2467` +- Restore ``ValidationError`` to be importable from + ``werkzeug.routing``. :issue:`2465` + Version 2.2.0 ------------- diff --git a/src/werkzeug/routing/__init__.py b/src/werkzeug/routing/__init__.py index 7f80e8386..04d092b01 100644 --- a/src/werkzeug/routing/__init__.py +++ b/src/werkzeug/routing/__init__.py @@ -112,6 +112,7 @@ from .converters import PathConverter from .converters import UnicodeConverter from .converters import UUIDConverter +from .converters import ValidationError from .exceptions import BuildError from .exceptions import NoMatch from .exceptions import RequestAliasRedirect diff --git a/src/werkzeug/routing/matcher.py b/src/werkzeug/routing/matcher.py index 3c649c2e6..c02812693 100644 --- a/src/werkzeug/routing/matcher.py +++ b/src/werkzeug/routing/matcher.py @@ -129,6 +129,22 @@ def _match( rv = _match(new_state, remaining, values + list(match.groups())) if rv is not None: return rv + + # If there is no match and the only part left is a + # trailing slash ("") consider rules that aren't + # strict-slashes as these should match if there is a final + # slash part. + if parts == [""]: + for rule in state.rules: + if rule.strict_slashes: + continue + if rule.methods is not None and method not in rule.methods: + have_match_for.update(rule.methods) + elif rule.websocket != websocket: + websocket_mismatch = True + else: + return rule, values + return None try: diff --git a/tests/test_routing.py b/tests/test_routing.py index dc0acfa6f..0c28a1867 100644 --- a/tests/test_routing.py +++ b/tests/test_routing.py @@ -219,6 +219,7 @@ def test_strict_slashes_leaves_dont_consume(): r.Rule("/path3/", endpoint="branch", strict_slashes=False), r.Rule("/path4", endpoint="leaf", strict_slashes=False), r.Rule("/path4/", endpoint="branch", strict_slashes=False), + r.Rule("/path5", endpoint="leaf"), ], strict_slashes=False, ) @@ -233,6 +234,7 @@ def test_strict_slashes_leaves_dont_consume(): assert adapter.match("/path3/", method="GET") == ("branch", {}) assert adapter.match("/path4", method="GET") == ("leaf", {}) assert adapter.match("/path4/", method="GET") == ("branch", {}) + assert adapter.match("/path5/", method="GET") == ("leaf", {}) def test_environ_defaults():