Connexion: Trailing slash endpoint routing returns 404

Created on 16 Nov 2016  路  13Comments  路  Source: zalando/connexion

Description

It's well-known that most HTTP routers deals with the trailing slash accordingly, matching both /foo or /foo/ path expressions.

I'm not sure if this was already supported and can be easily enabled programmatically.

Expected behaviour

Matching the route and reply with a 301 status and redirect it accordingly to /foo (however redirection can be optional).

Actual behaviour

404 status code is returned instead.

Steps to reproduce

Just make an HTTP request against a valid endpoint in a connext+Flask app adding a final slash.

Additional info:

  • python 3.5.2
  • connexion version 1.0.129

Most helpful comment

setting strict_slashes=False actually works like a charm, you need to set it before connexion_app.add_api is called

connexion_app = connexion.FlaskApp(__name__, specification_dir='swagger/')
app = connexion_app.app
app.url_map.strict_slashes = False  # it needs to be set before add_api call
connexion_app.add_api('swagger.yaml', resolver=RestyResolver('api'))

All 13 comments

Regarding redirects: this might cause issues if the request is not GET but PUT or POST...

Yes, I'd prefer do not redirecting requests, but some other implementations done that.

Apparently Flask, based on Werkzeug鈥檚 routing module, does a redirection only if a trailing slash is present, otherwise, the expected behaviour would be a 404 response error, as we have now.
See section "Unique URLs / Redirection Behavior":
http://flask.pocoo.org/docs/0.11/quickstart/#variable-rules

I suspect that connexion should register an additional route that matches the same route with a trailing slash, but I think that behaviour should not be supported by default, instead, it should be explicitly enabled, perhaps via a custom x-* directive at OpenAPI spec endpoint level.

What do you guys think?

Moreover, if we agree about an implementation strategy, I'm very open to provide a PR with the enhancement.

Any update on this?

We register the urls in the Flask app router exactly as defined in the provided OpenAPI specification. So it works as described in the Flask documentation.

I don't understand why we should change Connexion to do differently. Although, I am open to understand better why this should be changed.

One core reason would be for API UX quality reasons. Most HTTP API are still consumed by humans as the entry point and humans are more prone to make mistakes, such as adding a trailing slash. If you're using a web browser, the history is going to bother you too.

This is not an isolated issue: the majority of HTTP routers/path multiplexers fixes the trailing slash transparently for the developer.

I don't think forcing people adding boilerplate in their Swagger specs is going to help here.

Let's make things more human friendly :)

So the change would be If the URL ends without trailing slash and the operation is GET we append an / before registering the path in the Flask app router?

I think it can be as simple as:

  • Register any endpoint route for any HTTP verb within Flask app router for both trailing and non-trailing slash expression.

Also supporting the following conditions:

  • If the route defined in the Swagger spec already has a trailing slash (probably due to human mistake), normalize it first and process it accordingly as described above.
  • DO NOT support HTTP redirection at all.

The issue also comes when we create a unit test using app.app.test_client.
When u request /name instead of /name/
It gives 301 when unit test need to test the output from same endpoint.

And I tried using flask strict_slashes = False.
In connexion.app.app.url_mapping.strict_slash.

Its not working

I agree that connexion should at the very least respect the app.app.url_map.strict_slashes flag. I would actually argue that strict_slashes should be false by default; see e.g. Zalando's RESTful API Guidelines:

Must: Avoid Trailing Slashes
The trailing slash must not have specific semantics. Resource paths must deliver the same results
whether they have the trailing slash or not.

Is it not possible to follow this guideline with connexion?

setting strict_slashes=False actually works like a charm, you need to set it before connexion_app.add_api is called

connexion_app = connexion.FlaskApp(__name__, specification_dir='swagger/')
app = connexion_app.app
app.url_map.strict_slashes = False  # it needs to be set before add_api call
connexion_app.add_api('swagger.yaml', resolver=RestyResolver('api'))
Was this page helpful?
0 / 5 - 0 ratings