Werkzeug: Allow disabling appending slash?

Created on 31 Jan 2018  ·  7Comments  ·  Source: pallets/werkzeug

We're using Werkzeug and Flask to build a REST API. In this context, we don't want the automatic behavior of appending trailing slashes, as this leads to asymmetries between GETs and other requests, and it's acceptable to be strict here.

Currently we need to use a custom rule class that hooks into internals (Rule.match and RequestSlash) to do this.

Would it make sense to add this as a first-order config option? Something like append_slash (to parallel the equivalent Django option) on URL maps?

routing

Most helpful comment

app.url_map.strict_slashes = False is all you need to avoid this.

All 7 comments

app.url_map.strict_slashes = False is all you need to avoid this.

That actually makes the route match when the slash is missing. I want it to 404.

The current behavior is:

| Rule has trailing slash | Path has trailing slash | strict_slashes | Result |
|-|-|-|-|
| N | N | True | (match) |
| N | N | False | (match) |
| N | Y | True | 404 |
| N | Y | False | 404 |
| Y | N | True | 301 |
| Y | N | False | (match) |
| Y | Y | True | (match) |
| Y | Y | False | (match) |

I want a 404 when the rule has a trailing slash but the path doesn't. Right now I'm doing this via:

class StrictRule(Rule):
    def match(self, path, method=None):
        try:
            result = super(StrictRule, self).match(path, method)
        except RequestSlash:
            return None

        return result

But I'd rather not reach into internals.

Using the custom rule would be the correct way to do this in Flask. We have decided internally not to try and create everything for everyone in rules. If you want a rule beyond what is offered as standard you would need to specify it. For Flash, you would set the url_rule_class to the new class. See the Flask docs for more information. http://flask.pocoo.org/docs/1.0/api/?highlight=rule#flask.Flask.url_rule_class

@aenglander

Per https://github.com/pallets/werkzeug/issues/1246#issuecomment-362099342, the issue here is with the custom rule implementation. RequestSlash is explicitly marked as an internal exception per https://github.com/pallets/werkzeug/blob/a220671d66755a94630a212378754bb432811158/src/werkzeug/routing.py#L259-L260, so it's quite awkward to handle this in user code.

And while I certainly agree that base rules shouldn't support every possible use case, not matching on an absent trailing slash is a very common use case. For prior art, this pattern is one of the few that are explicitly exposed in Django config via APPEND_SLASH: https://docs.djangoproject.com/en/dev/ref/settings/#append-slash.

"Internal" in this case means "internally handled," not "for "internal use only." The code you've shown is fine to use.

@davidism Thank you for the clarification. I'll go ahead and go with this, then.

Was this page helpful?
0 / 5 - 0 ratings