Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bare path of '/' #247

Open
Climbgunks opened this issue Mar 4, 2017 · 14 comments
Open

Bare path of '/' #247

Climbgunks opened this issue Mar 4, 2017 · 14 comments

Comments

@Climbgunks
Copy link

I'm probably missing something obvious, but I can't seem to set a path of '/'

A simple test program that attempts to use both namespaces and swagger:

#!/usr/bin/python                                                                                            
from flask import Flask
from flask_restplus import Resource, Api

app = Flask(__name__)
api = Api(app, doc='/swagger/')

ns = api.namespace('Test', description='Test operations', path='/')
@ns.route('/hello', '/', '/world')
@ns.doc()
class Hello(Resource):
    def get(self):
        return { 'hello': 'World' }

if __name__ == '__main__':
    app.run(debug=True)

/hello and /world work as expected.... / throws a 404 whether through the swagger UI (http://localhost:5000/swagger) or through curl/wget

Assuming this isn't a bug in flask-restplus, how do I set an endpoint for '/'

@frol
Copy link
Contributor

frol commented Mar 4, 2017

@Climbgunks Here is the workaround:

#!/usr/bin/python                                                                                            
from flask import Flask
from flask_restplus import Resource, Api as BaseApi


class Api(BaseApi):

    def _register_doc(self, app_or_blueprint):
        # HINT: This is just a copy of the original implementation with the last line commented out.
        if self._add_specs and self._doc:
            # Register documentation before root if enabled
            app_or_blueprint.add_url_rule(self._doc, 'doc', self.render_doc)
        #app_or_blueprint.add_url_rule(self._doc, 'root', self.render_root)

    @property
    def base_path(self):
        return ''


app = Flask(__name__)
api = Api(app, doc='/swagger/')

ns = api.namespace('Test', description='Test operations', path='/')
@ns.route('/hello', '/', '/world')
@ns.doc()
class Hello(Resource):
    def get(self):
        return { 'hello': 'World' }

if __name__ == '__main__':
    app.run(debug=True)

flask-restplus implements _register_doc as follows:

    def _register_doc(self, app_or_blueprint):
        if self._add_specs and self._doc:
            # Register documentation before root if enabled
            app_or_blueprint.add_url_rule(self._doc, 'doc', self.render_doc)
        app_or_blueprint.add_url_rule(self._doc, 'root', self.render_root)

and here is the render_root:

    def render_root(self):
        self.abort(404)

Thus, I don't know how to override this handler other than subclassing the Api.

@Climbgunks
Copy link
Author

Climbgunks commented Mar 5, 2017

Thanks..that workaround works. Btw, the current code uses the line:
app_or_blueprint.add_url_rule(self.prefix or '/', 'root', self.render_root)
which still creates the problem. Actually, not sure why this line is needed ---
if there wasn't a url rule, then flask would return a 404 anyway.

it does set the 'root' for base_path, not sure of the side effect of not setting it...

@frol
Copy link
Contributor

frol commented Mar 5, 2017

Flask-restplus just needs a root name to resolve in a few places, so I had to patch base_path by using a hard-coded string where flask-restplus would use url_for('root')

@cizixs
Copy link

cizixs commented May 3, 2017

This is not a good user experience. Is there a future plan to improve this?
Users should be able to customize theirs own / index endpoint.

@dmig
Copy link

dmig commented Oct 10, 2017

I faced an issue similar to described here. Instead of request processing for route / in namespace I get redirect with code 301.
There is something special with this / route... Is there a way to handle /api/projects endpoint in projects namespace?

Request:

OPTIONS /api/projects HTTP/1.1
Host: my-test-server
Access-Control-Request-Method: GET
...

Response:

HTTP/1.1 301 MOVED PERMANENTLY
...
Location: http://my-test-server/projects/
...

I use namespaced api structure:

#! venv/bin/python
# -*- coding: utf-8 -*-

from flask_restplus import Api
from .Projects import api as projects
from .Dictionaries import api as dicts
# ... more of them ....

api = Api(
    title='my api',
    version='1.0'
)

api.add_namespace(projects)
api.add_namespace(dicts)
# ... more of them ....

Projects.py

#! venv/bin/python
# -*- coding: utf-8 -*-

from flask import request, json
from flask_restplus import Namespace, Resource, fields, abort

from db import DB
import logging

api = Namespace('projects', description='Projects related operations')

# ...

@api.route('/')  # <----- This one gives 301
class ProjectList(Resource):
    def get(self):
        pass

# ...

Dictionaries.py

#! venv/bin/python
# -*- coding: utf-8 -*-

from flask_restplus import Namespace, Resource, abort
import logging

api = Namespace('dicts', description='Dictionaries related operations')

# ...

@api.route('/grouped')  # <----- This one works as expected for url '/api/dicts/grouped'
class DictionaryGroup(Resource):
    def get(self):
        pass

@frol
Copy link
Contributor

frol commented Oct 10, 2017

@dmig This is odd. I have @api.route('/') in every module (here is my demo project) and it always works as expected.

@dmig
Copy link

dmig commented Oct 11, 2017

@frol I use path prefix /api, which flask detects properly.

E.g. this piece from previous version works as expected on /api/projects.

#!venv/bin/python
# -*- coding: utf-8 -*-

from flask import Flask, jsonify, make_response, request

# ...

@app.route('/projects', methods=['GET'])
def get_projects():
    pass
# ...

@dmig
Copy link

dmig commented Oct 11, 2017

@frol I found an answer. Werkzeug makes redirect trying to append slash at the end.
Looks like, in this case:

# ...
api = Namespace('projects')
@api.route('/')
# ...

route becomes /projects/ instead of /projects

@tdbear
Copy link

tdbear commented Dec 20, 2017

The problem is especially nasty when you try to DELETE /path on https, get 301 back with http (because you are running behind a TLS terminating load balancer) and your next DELETE /path/ fails flat on its face.

@hoou
Copy link

hoou commented Mar 10, 2018

@dmig i'm facing the same issue, so what is exactly the solution to this?

@dmig
Copy link

dmig commented Mar 10, 2018

Use all urls with / at the end in this project and ditch Flask in others in favor of Falcon/Pycnic

@delusionxb
Copy link

delusionxb commented Jul 3, 2018

Don't know if there is still someone checking this issue, but I found a simple solution.
Just define the route before adding app into api, and don't forget to set swagger ui path to somewhere else.

app = Flask(__name__)

@app.route('/')
def index():
  return render_template('index.html')

api = Api(app, doc='/api/)
ns = Namespace(name, description)
api.add_namespace(ns, path)

app.run()

@ww-stas
Copy link

ww-stas commented Aug 9, 2018

I got the same problem with / at the end of path. And I found two solutions:

  1. Use empty route e.g.
@api.route('')
  1. Set strict_slashes to False right after flask application has created
    app = Flask(__name__)
    app.url_map.strict_slashes = False

@adolli
Copy link

adolli commented Mar 4, 2019

It seems that redirecting to a URL ends with '/' is Werkzeug's behaviour. Take a look at this https://stackoverflow.com/questions/21050320/flask-301-response . It not about restplus.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants