Search code examples
python-3.xmod-wsgibottle

Python Bottle app in /app1 - am I forced to prepend "/app1" to all routes?


I developed a bottle app locally using run() with routes that all start with "/" per the tutorial and now I want to put it on a real server.

Docs at http://bottlepy.org/docs/dev/deployment.html suggest to use WSGIScriptAlias / /var/www/yourapp/app.wsgi, but I don't want the root of my site to be handled by a WSGI app. I want the root of the site to be handled by raw Apache, and only URLs under /app1 to be handled by WSGI.

So I set mine up as WSGIScriptAlias /app1 /var/www/app1/app1.py. It runs in the sense that I can see what I defined in the .py file under route('/') when I browse to server://app1, but none of the hyperlinks have /app1 prepended and the browser can't pick up my css files from /var/www/app1/css, etc.

Subject says it all. Am I forced to prepend "/app1" to all routes when I want the app to live in a subdirectory?

I was trying to future-proof myself because I foresee making /app2, /app3, etc. in future.

EDIT 1: For the sake of experimentation, I did try prepending /app1 to all routes. The result was even worse: every single address I try to browse to under /app1 gives a 404 error.


Solution

  • So to attempt this, I took a boilerplate code from below repo

    https://github.com/arsho/bottle-bootstrap

    And used that as a base. I figured out that it is possible to mount your app on a base url, if you follow few simple rules

    • Each HTML that you serve should define a base path using the HEAD tag. Like <base href="{{ APP_MOUNT_PATH }}">
    • The static references which needs to be relative to the base path should not start with / or ./. Like <script type="text/javascript" src="static/jquery.min.js"></script>. This will make sure the path is generated using the mount path your provided in the base tag
    • Take the mount path from the environment variables when starting the app

    So here are the updated files

    index.tpl

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="description" content="Bottle web project template">
        <meta name="author" content="datamate">
        <link rel="icon" href="static/favicon.ico"> 
        <base href="{{ APP_MOUNT_PATH }}">
        <title>Project</title>
        <link rel="stylesheet" type="text/css" href="static/bootstrap.min.css">
        <script type="text/javascript" src="static/jquery.min.js"></script>
        <script type="text/javascript" src="static/bootstrap.min.js"></script>  
    </head>
    <body>
        <!-- Static navbar -->
        <nav class="navbar navbar-default navbar-static-top">
            <div class="container">
                <div class="row">
                    <div class="navbar-header">
                        <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                            <span class="sr-only">Toggle navigation</span>
                            <span class="icon-bar"></span>
                            <span class="icon-bar"></span>
                            <span class="icon-bar"></span>
                        </button>
                        <a class="navbar-brand" href="#">Project</a>
                    </div>
                    <div id="navbar" class="navbar-collapse collapse">
                        <ul class="nav navbar-nav navbar-right">
                            <li><a href="navbar/">Home</a></li>
                            <li><a href="./">Github</a></li>
                            <li><a href="navbar-fixed-top/">Stackoverflow</a></li>
                        </ul>
                    </div><!--/.nav-collapse -->
                </div>
            </div>
        </nav>
        <div class="container">
            <div class="row">
                <div class="jumbotron">
                <h2>Welcome from {{data["developer_name"]}}</h2>
                    <p>This is a template showcasing the optional theme stylesheet included in Bootstrap. Use it as a starting point to create something more unique by building on or modifying it.</p>
                </div>
            </div>
            <!--./row-->
            <div class="row">
                <hr>
                <footer>
                    <p>&copy; 2017 {{data["developer_organization"]}}.</p>
                </footer>           
            </div>
        </div> 
        <!-- /container -->
    </body>
    </html>
    

    app.py

    from bottle import Bottle, run, \
         template, debug, static_file
    
    import os, sys
    
    dirname = os.path.abspath(os.path.dirname(__file__))
    
    app = Bottle()
    debug(True)
    
    @app.route('/static/<filename:re:.*\.css>')
    def send_css(filename):
        print("Sending", filename, dirname)
        return static_file(filename, root=dirname+'/static/asset/css')
    
    @app.route('/static/<filename:re:.*\.js>')
    def send_js(filename):
        print("Sending", filename, dirname)
        return static_file(filename, root=dirname+'/static/asset/js')
    
    @app.route('/')
    def index():
        data = {"developer_name":"Tarun Lalwani",
                "developer_organization":""}
        return template('index', data = data)
    
    @app.route('/tarun/')
    def tarun():
        data = {"developer_name":"Tarun Lalwani",
                "developer_organization":""}
        return template('index', data = data)
    
    
    if __name__ == "__main__":
        run(app, host='localhost', port = 8080)
    

    app.wsgi

    import os
    os.chdir(os.path.abspath(os.path.dirname(__file__)))
    
    import bottle
    from app import app
    application = bottle.default_app()
    
    mount_path = os.getenv("APP_MOUNT_PATH", "/")
    application.config['APP_MOUNT_PATH'] = mount_path
    
    application.mount(mount_path,  app)
    bottle.BaseTemplate.defaults['APP_MOUNT_PATH'] = mount_path
    

    And then I ran it using uwsgi

    [uwsgi]
    http = 127.0.0.1:3031
    chdir = /Users/tarunlalwani/Documents/Projects/SO/bottle-bootstrap
    pythonpath = .
    env = APP_MOUNT_PATH=/app2/
    wsgi-file = app.wsgi
    processes = 1
    threads = 1
    stats = 127.0.0.1:9191
    ; logto = ./uwsgi.log
    

    Now the app loads fine at http://localhost:3031/app2/ and ``http://localhost:3031/app2/tarun` which shows that base path works for both types of url

    Base URL

    Base URL + One Level

    All the code is available at below repo for your convenience

    https://github.com/tarunlalwani/python-bottle-base-url