I found and forked the following Flask/SQLAlchemy/Marshmallow example project:
https://github.com/summersab/RestaurantAPI
It works like a charm, and I used it to build a custom API. It runs flawlessly with:
python run.py
Then, I tried to run it on a proper webserver:
uwsgi --http localhost:5000 --wsgi-file run.py --callable app
However, I get 404 for all routes except /
, /api
, and /api/v1.0
. I tried it on Gunicorn with the same results leading me to believe that the problem is with the code, not the webserver config.
All of the following posts have the same problem, but from what I could tell, none of them had solutions that worked for me (I may have missed something):
Could someone look at the code in my repo and help me figure out what's wrong?
EDIT:
Per the response from @v25, I changed my run.py
to the following:
from flask import Flask, redirect, render_template
from app import api_bp
from model import db, redis_cache
from config import DevelopmentConfig, TestingConfig, BaseConfig, PresentConfig
app = Flask(__name__)
t = 0
def create_app(config_filename):
app.config.from_object(config_filename)
global t
if t == 0:
app.register_blueprint(api_bp, url_prefix='/api/v1.0')
t = 1
if config_filename != TestingConfig:
db.init_app(app)
redis_cache.init_app(app)
return app
@app.route('/')
@app.route('/api/')
@app.route('/api/v1.0/')
def availableApps():
return render_template('availableApp.html')
PresentConfig = BaseConfig
app = create_app(PresentConfig)
if __name__ == "__main__":
app.run(use_debugger=False, use_reloader=False, passthrough_errors=True)
I then ran this with uwsgi, and it works as expected:
uwsgi --http localhost:5000 --wsgi-file run.py --callable app
Thanks for your help!
This could quickly be solved by creating a new file wsgi.py
, with the following contents:
import run
PresentConfig = run.BaseConfig
app = run.create_app(PresentConfig)
Then execute with:
uwsgi --http localhost:5000 --wsgi-file wsgi.py --callable app
or...
gunicorn --bind 'localhost:5000' wsgi:app
Why does this work...
If you have a look inside the run.py
file, and note what's happening when you launch that directly with python
:
if __name__ == "__main__":
PresentConfig = BaseConfig
app = create_app(PresentConfig)
app.run(use_debugger=False, use_reloader=False, passthrough_errors=True)
you can see that app
is being created, based on the return value of the create_app
function which is passed a config. Note also that the create_app
function registers the "other URLs" as part of the api_bp
blueprint.
However the code inside this if
clause is never executed when the app is executed with uwsgi/gunicorn; instead the app
object which is imported is one without the other URLs registered.
By creating the wsgi.py
file above, you're doing all of this in a manner which can then be improted by the wsgi/gunicorn executable.
With that in mind, another way to fix this would be to change the last four lines of run.py
to look more like:
PresentConfig = BaseConfig
app = create_app(PresentConfig)
if __name__ == "__main__":
app.run(use_debugger=False, use_reloader=False, passthrough_errors=True)
You could then execute this with your original wsgi command.
It's worth noting this may break other code which does from run import app
and expects app
not to be the return value of create_app
(unlikely in this case).
Let me know if you need clarification on anything.