Reputation: 306
I am trying serve a (python3.8.2) dash
application using an Apache2 server and mod_wsgi
, but the application stops on the "Loading..." screen. The server returns 404s for each of dash's .js scripts. I've had no problems when using dash's built in local server... but I'm not trying to serve this locally.
I'd love some advice on where I'm going wrong, and how to get this particular app up and running.
I am able to serve both a simple python "hello world" and a simple flask app, using my setup. However, when creating a simple 'dash' app...
[Error] Failed to load resource: the server responded with a status of 404 (Not Found) ([email protected]_6_0m1598005008.8.7.min.js, line 0)
... followed by 12 more similar error messages...
[Error] Failed to load resource: the server responded with a status of 404 (Not Found) (dash_renderer.v1_6_0m1598005008.min.js, line 0)
[Error] ReferenceError: Can't find variable: DashRenderer
Global Code (hazcurves:28:103)
my apache access log shows a series of GET requests that got 404's (here's one example):
... "GET /_dash-component-suites/dash_renderer/dash_renderer.v1_6_0m1598005008.min.js HTTP/1.1" 404 495 "http://efehr-dev.ethz.ch/hazcurves/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15"
When I compared my served html to the html from a correctly functioning Dash app, I notice a difference in the .js names. It looks like mine are being mangled somehow.
Functioning version, from https://dash-gallery.plotly.host/dash-wind-streaming/
# Excerpt from functioning app's "footer" section
<footer>
<script id="_dash-config" type="application/json">{"url_base_pathname": null, "requests_pathname_prefix": "/dash-wind-streaming/", "ui": false, "props_check": false, "show_undo_redo": false}</script>
# [CGR] then there are several components here that are variations of the following
<script src="/dash-wind-streaming/_dash-component-suites/dash_renderer/dash_renderer.min.js?v=1.0.0&m=1583853516"></script>
<script id="_dash-renderer" type="application/javascript">
var renderer = new DashRenderer();
</script>
My app's Footer makes the request look scrambled, with query parameters absorbed into the filename.
<footer>
<script id="_dash-config" type="application/json">{"url_base_pathname": null, "requests_pathname_prefix": "/", "ui": false, "props_check": false, "show_undo_redo": false, "suppress_callback_exceptions": false, "update_title": "Updating..."}</script>
# [CGR] then there are several components here that are variations of the following
<script src="/_dash-component-suites/dash_html_components/dash_html_components.v1_0_3m1598005008.min.js"></script>
<script src="/_dash-component-suites/dash_renderer/dash_renderer.v1_6_0m1598005008.min.js"></script>
<script id="_dash-renderer" type="application/javascript">var renderer = new DashRenderer();</script>
</footer>
Now, these .js files [the unscrambled versions] DO exist, and _dash-component-suites
would seem to map to my .local/lib/python3.8/site-packages
folder.
So, from what I've shown above, I have .../dash_renderer/dash_renderer.min.js
and .../dash_html_components.min.js
A clean system with Ubuntu 20.04.1 LTS , Apache 2.4 with mod_wsgi installed.
Python 3.8.2 has the following relevant packages installed [using sudo pip3] :
dash==1.14.0
dash-core-components==1.10.2
dash-html-components==1.0.3
dash-renderer==1.6.0
dash-table==4.9.0
Flask==1.1.2
Flask-Compres=1.5.0
Here is my app.wsgi:
from flask import Flask
server = Flask(__name__)
import dash
import dash_core_components as dcc
import dash_html_components as html
app = dash.Dash(server=server)
app.css.config.serve_locally = True
app.scripts.config.serve_locally = True
app.layout = html.Div(children=["hello"])
application = app.server
I have been able to run this using the built-in server, serving it on a local server.
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /home/username/public_html
WSGIDaemonProcess /home/username/public_html/myapp.wsgi processes=2 threads=15 display-name=%{GROUP}
WSGIScriptAlias /hazcurves /home/username/public_html/myapp.wsgi
<Directory /home/username/public_html>
AllowOverride all
Options Indexes FollowSymLinks
<Limit GET POST OPTIONS>
Require all granted
</Limit>
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
alias _dash-component-suites/ .local/lib/python3.8/site-packages
After attempting to shoehorn advice from websites that are tangentially relevant towards this question, I'm starting to feel like a monkey banging on keys --- which feels, um wrong (and insecure)
final caveat: this is my first foray into managing an app on the server side of the world.
Upvotes: 4
Views: 3342
Reputation: 306
I am happy to report I eventually figured it out, and that my applications are running great now. Here's what I learned.
I had to be mindful of the following when calling dash.Dash(...)
Instead of what I had written, I needed:
dash.Dash(__name__, requests_pathname_prefix='/hazcurves/')
requests_pathname_prefix
needed to match the URL alias (starting end ending with slashes).app.css.config.serve_locally = True
app.scripts.config.serve_locally = True
So, here are excerpts from my three files of interest:
mysite.conf : /etc/apache2/sites-available/mysite.conf
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /home/myusername/public_html
# ---- Hazard Curves App ---- #
# using WSGIProcessGroup within Location allows multiple apps to run
# without interfering with each other
WSGIDaemonProcess curves/wsgi.py processes=2 threads=15 display-name=%{GROUP}
WSGIScriptAlias /curves /home/myusername/public_html/curves/wsgi.py
<Location /curves>
WSGIProcessGroup curves/wsgi.py
</Location>
</VirtualHost>
wsgi.py : ~/public_html/curves/wsgi.py
#!/usr/bin/python3
from myapp import app
application = app.server
myapp.py : ~/public_html/curves/myapp.py
import dash
import dash_html_components as html
app = dash.Dash(__name__, requests_pathname_prefix='/curves/')
app.title = "Some Test"
app.layout = html.Div([html.H6("Hello World")])
if __name__ == '__main__':
app.run_server(port=8051, debug=True) # unimportant in this context
Upvotes: 8