4

tldr; Looking for a way to prefix all routes within an app running behind Gunicorn without a reverse proxy/blueprint/duplicate prefix in @route(PREFIX + '/') etc.


Creating a few Python services, using Flask, running in Docker containers.
Not, currently, using Nginx/Apache for reverse proxying.

Have something like below that works when running flask by itself
- (eg % python app.py responds with localhost:5000/a/b/some-route)

base_path = "/a/b"
app = DispatcherMiddleware(_root_app, {base_path: self})
run_simple(host, port, app, **options)

Not sure how to achieve the same result when running behind Gunicorn.
(Would really like to do this without making a blueprint for the main app. Also trying to avoid having the same prefix in every @route(PREFIX + ''))

Reason for doing this
Using an extension that adds a few routes, along with a blueprint. Would like to have app routes AND extension/blueprint routes to all be prefixed.

This question asked this specifically for Flask, which I'm able to get working using the DispatcherMiddleware approach.
My question is how to get this working when running behind Gunicorn (no Nginx or Apache in front, just Gunicorn)


Potential Fix:

Currently using a subclass of Flask (needed to do some customized logging nonsense.)
Overriding the add_url_rule works.

prefixed_rule = self._prefix_rule(rule)
super().add_url_rule(prefixed_rule,
                     endpoint=endpoint,
                     view_func=view_func,
                     **options)

This also works with our extensions too

Community
  • 1
  • 1
Justin
  • 4,434
  • 4
  • 28
  • 37
  • Out of curiosity, what are you doing that requires multiple non-compossible services to be treated as one site? Is this for app dependency isolation? And why not have a reverse-proxy in front of them all? – Sean Vieira Sep 24 '15 at 03:04
  • It's just for some micro-services, so there will be a single endpoint. Just have some extensions that need to be prefixed as well as the app. We currently have a reverse proxy in place, but this may change going forward so we wanted to do this at the route level if/when it does change on us. – Justin Sep 28 '15 at 15:12

3 Answers3

5

If you are doing app composition, then you can use the DispatcherMiddleware trick you referenced. However, it sounds like you are trying to have a single service that is subpath mounted, but doesn't serve anything out of the "higher" paths at all.

There are several different ways to do this.

  1. Replace Flask.url_map._rules with a werkzeug.routing.Submount rule factory:

    from werkzeug.routing import SubPath
    
    app = Flask(__name__)
    
    # register blueprints and extensions
    # load config, etc.
    
    app.url_map._rules = SubPath(app.config['APPLICATION_ROOT'], app.url_map._rules)
    
  2. Replace Flask.url_rule_class:

    from werkzeug.routing import Rule
    
    app.url_rule_class = lambda path, **options: Rule(PREFIX + path, **options)
    
  3. Replace add_url_rule, as you suggest in your question.

Community
  • 1
  • 1
Sean Vieira
  • 155,703
  • 32
  • 311
  • 293
  • I originally had #3 implemented, quick/easy, but I do like #2 a bit more so I've gone with something similar (using a closure, in-place of the lambda. That dang PEP8 =) – Justin Sep 28 '15 at 15:16
0

You can prefix all routes for a specific blueprint

from flask import Blueprint
my_blueprint = Blueprint('my_blueprint_name', __name__, url_prefix='/my_prefix')

then for all your routes for the blue print simply put

@my_blueprint.route('my_route')

before the function definition and its route will be the url_prefix+my_route

You may find this link helpful http://flask.pocoo.org/docs/0.10/patterns/urlprocessors/

lv9
  • 361
  • 3
  • 4
  • I wanted to avoid doing this in a blueprint, as this wouldn't prefix other extensions being used in the app. – Justin Sep 23 '15 at 20:58
0

try this

app = dash.Dash(
    __name__,
    server=server,
    routes_pathname_prefix='/dash/'
)
assembler
  • 3,098
  • 12
  • 43
  • 84