noirbizarre / flask-restplus

Fully featured framework for fast, easy and documented API development with Flask
http://flask-restplus.readthedocs.org
Other
2.73k stars 507 forks source link

Error while loading Flask-restplus swagger UI in kubernetes environment #305

Open optimus-kart opened 7 years ago

optimus-kart commented 7 years ago

I am using flask_restplus 0.10.1 for swagger api documentation in the python project. The configuration I am using is as below

api = Api(app,version='1.0',title='Flask Rest Api',description='Test') 
ns = api.namespace('Test', description='Test operations')

The swagger UI loads fine when running the service locally using python -m testproject,

But when the application is deployed on a docker container (in a kubernetes environment) which is running on a host the UI doesn't seem to load, but all the rest api work fine,

From chrome developer tool console I can see the following error jquery-1.8.0.min.js:1 Uncaught SyntaxError: Unexpected token < jquery.slideto.min.js:1 Uncaught SyntaxError: Unexpected token < jquery.wiggle.min.js:1 Uncaught SyntaxError: Unexpected token < jquery.ba-bbq.min.js:1 Uncaught SyntaxError: Unexpected token < handlebars-4.0.5.js:1 Uncaught SyntaxError: Unexpected token < lodash.min.js:1 Uncaught SyntaxError: Unexpected token < backbone-min.js:1 Uncaught SyntaxError: Unexpected token < swagger-ui.min.js:1 Uncaught SyntaxError: Unexpected token < highlight.9.1.0.pack.js:1 Uncaught SyntaxError: Unexpected token < highlight.9.1.0.pack_extended.js:1 Uncaught SyntaxError: Unexpected token < jsoneditor.min.js:1 Uncaught SyntaxError: Unexpected token < marked.js:1 Uncaught SyntaxError: Unexpected token < swagger-oauth.js:1 Uncaught SyntaxError: Unexpected token < (index):36 Uncaught ReferenceError: $ is not defined at (index):36 (anonymous) @ (index):36 The problem here is that the host adds a prefix to the rest api's defined in the python project. But when the request for swagger ui is sent the swagger ui doesn't seem to load it using the prefix. Instead of requesting for swagger.json like http://host/prefix/swagger.json The request is sent as http://host/swagger.json Similary the requests for all css and other files are sent and they fail,If I manually request http://host/prefix/swagger.json  I do get a valid json response.

Is there any specific configuration that has to be done for swagger to work in this kind of environment?

MarlieChiller commented 6 years ago

exact same issue that im having!

ziirish commented 6 years ago

This has nothing to do with flask-restplus, besides it has been answered a couple of times already.

Anyway, in most of the cases, when your application runs behind a reverse-proxy (which is probably the case here although I don't have a significant experience with k8s), you need to tell your application so.

Here is the Flask documentation.

But in your case, the ProxyFix might not work properly due to the fact your application is hosted behind an "alternative root path" (ie. /prefix). Thankfully, there is a documented workaround here. Now, as you may not have access to the reverse-proxy configuration to add such headers, you may want to write your own Fixer this way:

MY_PREFIX = '/prefix'

class ReverseProxied(object):
    '''Wrap the application in this middleware and configure the 
    front-end server to add these headers, to let you quietly bind 
    this to a URL other than / and to an HTTP scheme that is 
    different than what is used locally.

    :param app: the WSGI application
    '''
    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):
        script_name = MY_PREFIX
        environ['SCRIPT_NAME'] = script_name
        path_info = environ['PATH_INFO']
        if path_info.startswith(script_name):
            environ['PATH_INFO'] = path_info[len(script_name):]

        scheme = environ.get('HTTP_X_SCHEME', '')
        if scheme:
            environ['wsgi.url_scheme'] = scheme
        return self.app(environ, start_response)

And then fix your app this way:

app = Flask(__name__)
app.wsgi_app = ReverseProxied(app.wsgi_app)
MarlieChiller commented 6 years ago

thanks for the speedy reply, and sorry for the delayed reply on my part. this fixed my issue. thanks a lot

Formartha commented 5 years ago

Hi folks, tried this fix but without using "MY_PREFIX = '/prefix'" value. it didn't worked well for me. do we need it for k8s deployment or we can just use the documentation listed in @ziirish comment?

doanand commented 1 year ago

This has nothing to do with flask-restplus, besides it has been answered a couple of times already.

Anyway, in most of the cases, when your application runs behind a reverse-proxy (which is probably the case here although I don't have a significant experience with k8s), you need to tell your application so.

Here is the Flask documentation.

But in your case, the ProxyFix might not work properly due to the fact your application is hosted behind an "alternative root path" (ie. /prefix). Thankfully, there is a documented workaround here. Now, as you may not have access to the reverse-proxy configuration to add such headers, you may want to write your own Fixer this way:

MY_PREFIX = '/prefix'

class ReverseProxied(object):
    '''Wrap the application in this middleware and configure the 
    front-end server to add these headers, to let you quietly bind 
    this to a URL other than / and to an HTTP scheme that is 
    different than what is used locally.

    :param app: the WSGI application
    '''
    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):
        script_name = MY_PREFIX
        environ['SCRIPT_NAME'] = script_name
        path_info = environ['PATH_INFO']
        if path_info.startswith(script_name):
            environ['PATH_INFO'] = path_info[len(script_name):]

        scheme = environ.get('HTTP_X_SCHEME', '')
        if scheme:
            environ['wsgi.url_scheme'] = scheme
        return self.app(environ, start_response)

And then fix your app this way:

app = Flask(__name__)
app.wsgi_app = ReverseProxied(app.wsgi_app)

Worked like a charm in my case.