So I've been working w/ Flask & Bootstrap on an Apache Server. I've gotten it to the point where I can access the app & render the "first" or "main" template w/ the following route:
from view.py:
@app.route('/')
def fn_home():
return render_template("main.html")
Unfortunately, every attempt to route to another webpage/function from main.html fails. I'm using the "url_for" function in the navbar list href, attempting to get flask to supply the xls-upload.html webpage to Apache.
from main.html:
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="{{ url_for('upload') }}">Upload Spreadsheets </a></li>
from view.py:
@app.route('/upload')
def upload():
return render_template("xls-upload.html")
It looks like the function is being engaged, since the URL changes to http://myapp/upload, but the html page is NOT rendered/returned by the function - instead I receive a 404 "Not Found". I can't seem to return anything from the function, even return "Hello World"
.
It "seems" like Apache is really trying to resolve the http://myapp/upload path, rather than having a socket opened up to the Flask application through which the html is then sent. I'm not sure if this is a FCGI problem, if I'm missing a relative/absolute path issue, misunderstanding how Flask works in general, or some combination of all, etc.
I'm new to Flask so I'm hoping that someone could help me along the way since I really feel I've come to a dead end.
Thanks in advance!
My flask app is structured as follows:
Here are my applicable files:
1) /etc/httpd/conf.d/myapp:
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/cgi-bin/myapp/static/
ServerName myapp
Alias /static/ /var/www/cgi-bin/myapp/static/
ScriptAlias / /var/www/cgi-bin/myapp/start.fcgi
<Directory "var/www/cgi-bin/myapp">
AllowOverride None
Order allow,deny
Allow from all
AuthType Basic
AuthUserFile /etc/httpd/.htpasswd
AuthName 'Enter Password'
Require valid-user
</Directory>
</VirtualHost>
2) /var/www/cgi-bin/myapp/start.fcgi:
#!/usr/bin/python
# IMPORTS:
from flup.server.fcgi import WSGIServer
from view import app
if __name__ == '__main__':
WSGIServer(app).run()
3) /var/www/cgi-bin/myapp/view.py:
#!/usr/bin/python
# IMPORTS:
import os
from flask import Flask, render_template, url_for, request, session, redirect
from werkzeug import secure_filename
# STATIC VARIABLES
UPLOAD_FOLDER = 'var/www/cgi-bin/myapp/xls-dir'
ALLOWED_EXTENSIONS = set(['xls'])
## flask:
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
# FUNCTIONS
def fn_allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS
@app.route('/')
def fn_home():
return render_template("main.html")
@app.route('/upload')
def upload():
return render_template("xls-upload.html")
#return "HI there"
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0')
4) /var/www/cgi-bin/myapp/templates/main.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title>ALG Tools HOME</title>
<!-- Bootstrap -->
<link href="{{ url_for('static', filename = 'css/bootstrap.min.css') }}" rel="stylesheet">
</head>
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<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="/">ALG Tool - HOME</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="{{ url_for('upload') }}">Upload Spreadsheets </a></li>
<li><a href="/xls-download.html">Download Spreadsheets</a></li>
<li><a href="/cfg-generate.html">Generate Configs</a></li>
</ul>
</div>
</div>
</nav>
<body>
<h2>ALG stuff</h2>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="{{ url_for('static', filename = 'js/bootstrap.min.js') }}"></script>
</body>
</html>
5) /var/www/cgi-bin/myapp/templates/xls-upload.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title>ALG XLS Upload</title>
<!-- Bootstrap -->
<link href="{{ url_for('static', filename = 'css/bootstrap.min.css') }}" rel="stylesheet">
</head>
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<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="/">ALG Tool - HOME</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="/xls-upload.html">Upload Spreadsheets </a></li>
<li><a href="/xls-download.html">Download Spreadsheets</a></li>
<li><a href="/cfg-generate.html">Generate Configs</a></li>
</ul>
</div>
</div>
</nav>
<body>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="{{ url_for('static', filename = 'js/bootstrap.min.js') }}"></script>
</body>
</html>
FCGI isn't really a recommended way to serve a Python web app. You should look into one of the many ways of running WSGI.
However, assuming you need to do this for some reason, you have a minor configuration issue which is the cause of your problem; you need a trailing slash on the ScriptAlias path.
ScriptAlias / /var/www/cgi-bin/myapp/start.fcgi/
With this, Apache will pass the full path to the start.fcgi script, instead of replacing it.
Note that even with FCGI, you should not put your app code in cgi-bin. It doesn't need to be there, as it isn't run by the web server like a CGI app. In fact, your code should not even be under /var/www at all.