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

[question] base_path base_url problems with swagger interface #1117

Closed
safaribaer opened this issue Jan 6, 2020 · 4 comments
Closed

[question] base_path base_url problems with swagger interface #1117

safaribaer opened this issue Jan 6, 2020 · 4 comments

Comments

@safaribaer
Copy link

Hello,
I am following these tutorials for a small test project:
https://realpython.com/flask-connexion-rest-api/
https://stackoverflow.com/questions/18967441/add-a-prefix-to-all-flask-routes
My project requires a base_url. In this case testproj01 .
My data endpoint is available as: http://0.0.0.0:5000/testproj01/dyna/people This is ok.
The non swagger/connexion endpoint http://0.0.0.0:5000/testproj01/ is ok too.
The swagger ui is available as http://0.0.0.0:5000/testproj01/dyna/ui/ and is working but it tries to load the json file from http://0.0.0.0:5000/dyna/swagger.json and that is not working.
The base_url is missing in the link. If I change the link manually to: http://0.0.0.0:5000/testproj01/dyna/swagger.json I can see the endpoint deffinitions.
How can I tell connexion to also introduce the base_url into this request?
This is the code I am using:

#!/usr/bin/env python3

import connexion
import sys
import os
sys.path.append(os.path.join(os.path.dirname(__file__), 'dyna'))


class PrefixMiddleware(object):
    def __init__(self, app, prefix=''):
        self.app = app
        self.prefix = prefix

    def __call__(self, environ, start_response):
        if environ['PATH_INFO'].startswith(self.prefix):
            environ['PATH_INFO'] = environ['PATH_INFO'][len(self.prefix):]
            environ['SCRIPT_NAME'] = self.prefix
            return self.app(environ, start_response)
        else:
            start_response('404', [('Content-Type', 'text/plain')])
            return ["This url does not belong to the app.".encode()]


# set base url
base_path = "/testproj01"

# Create the application instance
app = connexion.App(__name__, specification_dir='./conf/')
app.debug=True
app.app.wsgi_app = PrefixMiddleware(app.app.wsgi_app, prefix=base_path)
# Read the swagger.yml file to configure the endpoints
app.add_api('swagger.yml', base_path="/dyna")

# Create a URL route in our application for "/"
@app.route('/')
def home():
    return " Hallo !"


# If we're running in stand alone mode, run the application
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

  


Python version: Python 3.8.1
Connexion version: Version: 2.3.0
@Norbo11
Copy link

Norbo11 commented Jan 7, 2020

I also recently suffered from this issue.

Swagger UI requires you to specify a URL where your spec file exists. Connexion automatically populates this with whatever you specify as the base_path inside your add_api call.

In our case, the web server hosts our application on a prefix such as your /testproj01 and hence this also needs to present on the URL inside Swagger UI (as that is the request your browser will send to fetch the spec), but not inside the base_path that we pass into add_api (because including it here would put your app inside /testproj01/testproj01).

I managed to solve this by reaching into the internals that connexion uses to determine the URL passed to swagger UI, so in your case:

api = app.add_api('swagger.yml', base_path="/dyna")
api._handlers.base_path = "/testproj01/dyna/v1"

A way to do this properly is for connexion to support a swagger-UI-specific base path.

@dtkav
Copy link
Collaborator

dtkav commented Jan 15, 2020

Actually in 2.5.1 there is added support for path-altering reverse proxies.
See #823 for the PR.

There's an example here: https://github.com/zalando/connexion/tree/master/examples/openapi3/reverseproxy

@safaribaer
Copy link
Author

@dtkav
Sorry but I cant get it to work. I do not have apath altering proxy. I tried to reuse the example but It is not working. I use this:

`
class PrefixMiddleware(object):
    def __init__(self, app, script_name=None, scheme=None, server=None):
        self.app = app
        self.script_name = script_name
        self.scheme = scheme
        self.server = server

    def __call__(self, environ, start_response):
        script_name = environ.get('HTTP_X_FORWARDED_PATH', '') or self.script_name
        # script_name = self.script_name
        if script_name:
            environ['SCRIPT_NAME'] = "/" + script_name.lstrip("/")
            path_info = environ['PATH_INFO']
            if path_info.startswith(script_name):
                environ['PATH_INFO_OLD'] = path_info
                environ['PATH_INFO'] = path_info[len(script_name):]
        scheme = environ.get('HTTP_X_SCHEME', '') or self.scheme
        if scheme:
            environ['wsgi.url_scheme'] = scheme
        server = environ.get('HTTP_X_FORWARDED_SERVER', '') or self.server
        if server:
            environ['HTTP_HOST'] = server
        return self.app(environ, start_response)

        # if environ['PATH_INFO'].startswith(self.prefix):
        #     environ['PATH_INFO'] = environ['PATH_INFO'][len(self.prefix):]
        #     environ['SCRIPT_NAME'] = self.prefix
        #     return self.app(environ, start_response)
        # else:
        #     start_response('404', [('Content-Type', 'text/plain')])
        #     return ["This url does not belong to the app.".encode()]


It works as long I use it locally. The swagger url in the web ui is wrong because it is missing testproj01. It works beacause PrefixMiddleware takes care of this. It is not working if I use it behind a reverse proxy. This is the relevant part in the proxy configuration:

    # testproj01 
    # proxy for testproj01 
    location /testproj01/ {
        proxy_pass         http://192.168.xxx.xxx:5000;
        proxy_redirect     off;
        proxy_set_header   Host             $host;
        proxy_set_header   X-Real-IP        $remote_addr;
        proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
    }

@safaribaer
Copy link
Author

safaribaer commented Jan 28, 2020

@Norbo11
Thank you for your help. It is an ugly solution but it works very well.
Regards

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

4 participants