Search code examples
javascriptflaskiis-10

How to put JavaScript files somewhere other than /static for a Flask app running on IIS?


I am trying to run a Flask server using IIS and load JavaScript files from somewhere other than /static since I want to organize my project on a per-page basis rather than dumping everything into /static and /templates. Previous questions such as this one don't solve my question as they deal with just loading the JavaScript from /static, which is not what I am looking for. Neither this or this worked for me.

I am not using jQuery and my index.html is in /, not /templates

The index.html and index.js files contain the same information for both the server run by Flask directly and the one run with IIS. The IIS server is running on 192.168.1.87:80, the host machine's IP address.

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div style="width: 300px; height: 300px; background-color: red;" id="testDiv"></div>

    <!--<script src="index.js"></script>-->
    <!-- <script src="/static/index.js"></script> -->
    <!-- <script src="/stuff/index.js"></script> -->
    <!-- <script src="{{ url_for('static', filename='index.js') }}"></script> -->
    <!-- <script src="/index.js"></script> -->
     <!--<script src="../stuff/index.js"></script> -->
    <script src="../index.js"></script>
</body>
</html>

index.js

document.getElementById("testDiv").style.backgroundColor = "blue";

app.py run with just Flask

from flask import Flask, send_from_directory

app = Flask(__name__, static_folder="stuff")
#app = Flask(__name__, static_folder="")
#app = Flask(__name__)

@app.route("/")
def index():
    return send_from_directory("", "index.html")

if __name__ == '__main__':
    app.run()

app.py run with the IIS server

from flask import Flask, send_from_directory
app = Flask(__name__)

@app.route("/")
def index():
   return send_from_directory("", "index.html")

if __name__ == '__main__':
   app.run(host="192.168.1.87", port=80)

All errors returned by website run by IIS are GET http://192.168.1.87/index.js net::ERR_ABORTED 404 (NOT FOUND) and all error from the standalone Flask server are 404s. All errors relate to the script call in index.html.

I have not gotten send_from_directory or url_for to work with the JavaScript on either the Flask or IIS servers, which were recommended here (in both cases, the server returns a 404).

On the standalone Flask server, having static_folder="stuff" and the script call in index.html be <script src="/stuff/index.js"></script> correctly loads the JavaScript from /stuff and having static_folder be anything other than "stuff" with a script call in index.html be <script src="/static/index.js"></script> correctly loads the JavaScript from /static, but having static_folder="stuff" and the script call be <script src="/static/index.js"></script> and vice versa causes a 404 error.

Using <script src="/static/index.js"></script> with static_folder not set to "stuff" on the IIS server correctly loads the JavaScript from /static, but does not work when the same setup as above is done with /stuff.


Solution

  • I have no experience with IIS, but it seems to me that you have a misunderstanding of templates and static files.

    Templates with expressions such as url_for() are in a directory chosen when the application is instantiated (template_folder is by default templates) and require a call to render_template() within the endpoint.

    # Serve 'index.js' from the static-folder with a template called 'index.html'.
    # The URL to 'index.js' is 'http://127.0.0.1:5000/static/index.js'.
    
    app = Flask(__name__, template_folder='templates')
    
    @app.route('/')
    def index():
        return render_template('index.html', **locals())
    
    <script src="{{ url_for('static', filename='index.js') }}"></script>
    
    |- app.py
    |- static
    |  |- index.js
    |  |- ...
    |
    |- templates
       |- index.html
    

    Static files are in a separate directory (by default static). The directory can also be changed during instantiation by specifying static_folder. Furthermore, the path prefix of the URL can be changed by specifying static_url_path. For example, you can assign '' to serve files that are in the static folder but should be served without a prefix such as static in the URL.
    If you want to return a static file within an endpoint, app.send_static_file() is preferable to send_from_directory().
    Note that Jinja, Flask's template engine, does not take these static files into account.

    # Serve 'index.js' from the static-folder with a static file called 'index.html'.
    # The URL to 'index.js' is 'http://127.0.0.1:5000/index.js'.
    
    app = Flask(__name__, 
        static_folder='static', 
        static_url_path=''
    )
    
    @app.route('/')
    def index():
        return app.send_static_file('index.html')
    
    <script src="/index.js"></script>
    
    |- app.py
    |- static
    |  |- index.html
    |  |- index.js
    |  |- ...
    |
    |- templates