fix(api): strip trailing slash on namespace path to avoid '//' rules (#74)#652
Open
jbbqqf wants to merge 1 commit into
Open
fix(api): strip trailing slash on namespace path to avoid '//' rules (#74)#652jbbqqf wants to merge 1 commit into
jbbqqf wants to merge 1 commit into
Conversation
…ython-restx#74) When add_namespace(ns, path=path) is called with path='/' (the documented way to mount a namespace at the root) or with any path ending in '/', ns_urls did a naive 'path + url' concatenation, producing rules like '//foo' or '/api//foo'. Normalise the join by stripping the trailing slash on the prefix. Add a regression test that asserts no rule in app.url_map ends up with '//' for path='/' and path='/api/'. Fixes python-restx#74.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Api.add_namespace(ns, path=path)(andApi.ns_urls) builds rulepaths by naive concatenation:
That is fine for
path='/api'+/foo=/api/foo, but the moment thecaller does what the docs lead them to — mount a namespace at the API
root with
path='/'— the rule ends up as'//foo'.path='/api/'produces
'/api//foo'. Both shapes leak intoapp.url_mapand breakurl_for/Swagger reverse routing.Fix: normalise the prefix with
rstrip('/')before concatenating, so'/' + '/foo'becomes'' + '/foo' = '/foo'and'/api/' + '/foo'becomes
'/api' + '/foo' = '/api/foo'. This matches the normalisationalready done on
Namespace.pathfor the namespace's own_path(
flask_restx/namespace.py:67).Fixes #74.
Context
The OP (gyre) raised this in 2020 wanting to do exactly what the
flask-restx docs recommend for a root-mounted namespace:
flask_restx/namespace.py:65-67already does(self._path or ("/" + self.name)).rstrip("/")precisely because ofthis concern. The bug is that
ns_urlsbypasses that normalisationwhen the caller specifies a path explicitly via
add_namespace.Changes
flask_restx/api.py:486-491: addpath = path.rstrip("/")inns_urls, with a comment explaining why.tests/test_api.py: regression testtest_ns_path_no_double_slashexercising both
path="/"andpath="/api/". It asserts there is no'//'substring in any URL rule and thaturl_forreturns theexpected canonical form.
CHANGELOG.rst: 1.3.3 bug-fix entry.Reproduce BEFORE/AFTER yourself (copy-paste)
What I ran locally
The single failure (
tests/test_inputs.py::URLTest::test_check) ispre-existing on
masterand unrelated to this change — it depends onhttp://this-domain-should-not-exist.comactually not resolving, whichis no longer true on the network where the suite ran.
Edge cases
"/""/foo""/foo"(was"//foo")"/api/"(trailing /)"/foo""/api/foo"(was"/api//foo")"/api_test"(no trailing /)"/test/""/api_test/test/"(unchanged)test_ns_path_prefixesns.pathalreadyrstrip('/')-edRisk / blast radius
The only behavioural change is that prefix paths ending in
/losetheir trailing
/before being concatenated with the resource URL.This is purely a normalisation: before this PR those paths produced
syntactically broken
'//'rules that Flask still accepts but thatbreak
url_forround-tripping and look wrong in Swagger. There is nocaller that was deliberately relying on
'//foo'as a valid path.The existing
test_ns_path_prefixes(which uses"/api_test"with notrailing slash) still passes unchanged.
PR drafted with assistance from Claude Code (Anthropic). The change was
reviewed manually against
flask-restx's source. The reproducer blockabove is the one I used during development; reviewers can paste it
verbatim.