I am writing a flask application with angularjs frontend. Templates aren't rendered from flask yet, they are delivered as static files. My api endpoint is as following:
#route('/projects', method=['GET'])
def rest_projects(self):
"""TODO: CRUD operation
"""
Project = Model.get('project.project')
return jsonify([project.serialize() for project in Project.search([])])
url_for works perfect with above endpoint but is there any way it can build urls for js templates also?. Say if url has api prefixed ie: /api/tasks, it comes to this handler otherwise deliver template. Right now I am using nginx to achieve that but is there anything wrong in my design which is restricting me to do this from flask only.
Flask is not intended to serve static files and you are better off keeping your files served by nginx for performance reasons. You can serve static files however using send_static_file() or send_from_directory methods.
http://flask.pocoo.org/docs/0.10/api/
Related
I want to restrict files to be available to logged in users, but otherwise return a 403 error or similar. For example a user should be able to view/download /static/data/example.csv only if they're logged in.
I know how to control the actual displaying of the files using Flask-Login if they're not logged in, but not how to block access to the file if they visit the link directly in their browser.
Flask adds a static route to serve static files. When you're in production, you typically "short circuit" this route so that Nginx serves the files before the request ever gets to your app. Instead of adding this "short circuit", leave it out and let Flask handle the requests. Overwrite the static route with one that is wrapped by Flask-Login's login_required.
from flask_login import login_required
app.view_functions['static'] = login_required(app.send_static_file)
This is typically overkill though, since you want truly static files to be served no matter what so that pages look right to non-logged in users (otherwise the CSS wouldn't even be sent for the login page). Instead, "short circuit" the static folder to be served by Nginx, and define a route that will serve protected files from some other directory, such as the instance folder. See flask.send_from_directory.
import os
from flask import send_from_directory
from flask_login import login_required
#app.route('/protected/<path:filename>')
#login_required
def protected(filename):
return send_from_directory(
os.path.join(app.instance_path, 'protected'),
filename
)
This will serve files from the directory "protected" in the instance folder to logged in users only. Other restrictions could also be added, such as only allowing certain users access to certain files. Similar to the static path, you can generate a url to a file with:
url_for('protected', filename='data/example.csv')
I am running Flask and Eve on localhost at a same time. The Flask app serves static files and makes requests to the Eve app to get some data. I want to run Eve only, without a separate Flask app. How can I serve static files with Eve?
A better approach will be to prefix the /api for all REST APIs. This can be done by adding URL_PREFIX="api" in settings.py.
By doing this whenever there is request to /, Eve(Flask) will not return the resource catalog instead returns the page as given in run.py.
To serve static content add route decorators accordingly in run.py,
#app.route('/')
def index():
return app.send_static_file('index.html')
app.run(host="0.0.0.0", debug=True)
Eve is a Flask application (a subclass) so as a general rule everything that works with Flask works with Eve too. You could register a blueprint, or add new routes.
Also see this answer for a link to a working example: Servicing html requests with Eve
try set import_name arg for Eve:
app = Eve(import_name=__name__)
I have a single page app (SPA) that I am running off of Google App Engine (GAE). GAE does three things:
Serve the index.html file
Serve the static files (JS, CSS, etc.)
Serve the dynamic files (images, text, etc. via REST)
I use the following app.yaml configuration.
handlers:
- url: /app
static_dir: app
- url: /.*
script: main.app
My understanding is that this should match any requests going to the /app folder, which would serve my static files. All the REST services and the main index page would then be caught by the /.* and processed by main.py
However, I see the following behavior:
If I remove the /app handler, I can successfully serve the index.html (via Jinja templating) and the REST services (such as localhost/subjects/). However, I cannot see the static files (as expected).
If I add the /app handler, the index.html file does not serve and gives an "Internal Server Error" IOError(errno.EACCES, 'file not accessible', filename). However, when I request a static file such as: "localhost/app/app.js", this succeeds.
Is there something that I am missing here? I do not understand why the two would conflict.
Thank you!
Notes:
Google App Engine 1.8.9, Python 2.7, Developing locally
EDIT:
Here is the Python code I am using to serve the page
path = os.path.join(os.path.dirname(__file__), 'app')
jinja_environment = jinja2.Environment(loader=jinja2.FileSystemLoader(path))
class MainHandler(webapp2.RequestHandler):
def get(self):
self.response.headers['Content-Type'] = 'text/html'
template_values = {}
template = jinja_environment.get_template('index.html')
self.response.out.write(template.render(template_values))
My directory structure is as follows:
/
main.py
app.yaml & etc.
app
index.html
app.js
Module A
moduleA.tpl.html
moduleA.js
Edit 2:
I moved index.html to the root directory (/), and then used the following python code:
path = os.path.dirname(__file__)
jinja_environment = jinja2.Environment(loader=jinja2.FileSystemLoader(path))
It (very interestingly) appears that the "double mapping" of the index.html to the Jinja template and to the static directory files caused a problem. I wonder what is the best practice way to do this. I use Jinja for one reason: adding the (GAE generated) login/logout link to the index.html file. Other than that, there is no reason for using it.
Python is by default unable to access files or directories marked as static in App Engine. You can add application_readable: true to your handler mapping to enable this.
Another solution would be to move the index.html away from the static folder since it in fact isn't a static file but instead is a jinja template for Python.
Related question: Read a file on App Engine with Python?
Documentation for application_readable
application_readable
Optional. By default, files declared in static file handlers are uploaded as static data and are only served to end users, they cannot be read by an application. If this field is set to true, the files are also uploaded as code data so your application can read them. Both uploads are charged against your code and static data storage resource quotas.
Ok it might explains scenario #2, how do you access the index.html? If you add /app to the handler and your request url is something like /app/index.html it will serve from the static dir.
On your scenario seems your url does not contain /app, therefore it goes to the 2nd handler rule which is going to main.app.
However since you put the html inside the /app, appengine treats it as static file. If you want to serve the file from jinja template, you should not put it in static_dir
Jinja templates (or Django, Mako etc) has no requirement to be sat in a publicly accessible folder. They are always called via a handler in the application and compiled in the application before serving to the user.
It is common practice to put these in a /templates directory. There is no reference to this directory in app.yaml, it is purely used internally to serve the templates. Check out some of the boilerplate apps in github (search 'gae boilerplate'). This one is minimal and uses Jinja so may be a good example for you https://github.com/SoulAuctioneer/notvanillae
This question already has answers here:
Static files in Flask - robot.txt, sitemap.xml (mod_wsgi)
(10 answers)
Closed 8 years ago.
I've read on quiet a few places that serving static files should be left to the server, for example in a couple of the answers on this SO question. But I use the OpenShift PaaS, and can't figure out how to modify the .htaccess file there.
I came across this piece of code that serves the sitemap from a template. I did that on my app for both the sitemap, and robots.txt, like so -
#app.route("/sitemap.xml")
def sitemap_xml():
response= make_response(render_template("sitemap.xml"))
response.headers['Content-Type'] = 'application/xml'
return response
#app.route("/robots.txt")
def robots_txt():
return render_template("robots.txt")
Is there any harm in this, or is my approach okay?
Put robots.txt and sitemap.xml into your app's static directory and define this view:
from flask import Flask, request, send_from_directory
#app.route('/robots.txt')
#app.route('/sitemap.xml')
def static_from_root():
return send_from_directory(app.static_folder, request.path[1:])
Flask has built in support for serving static files.
Make a /static directory and put your files there. Then, when you instantiate Flask, specify the static_url_path parameter:
app = Flask(__name__, static_url_path='/')
The default is to serve static files from the /static/ path, but you want them served from / so they are where expected.
See the Flask API Docs for more info.
In addition to overhead and unnecessary code, the problem with your approach is if / when one of the files you want to serve contains something that looks like a template tag to render_template -- you can cause a rendering error. If you were to read the file into memory (once, not inside the method) then use that string as the body of the response without calling render_template, you would at least avoid that problem.
The best way is to set static_url_path to root url
from flask import Flask
app = Flask(__name__, static_folder='static', static_url_path='')
I am developing a SAAS application using Flask and I want users to be able to create/upload/use their own custom templates. This is how my directory structure is right now :
/flaskapp
/application.py
/static
/style.css
/templates (site & admin templates goes here)
/hello.html
/userdata
/user1
/template1
hello.html
/template2
hello.html
/user2
/template1
hello.html
/template2
hello.html
I am able to serve user specified templates using a solution found through this stackoverflow question : How to dynamically select template directory to be used in flask? but how do I serve static files from template directory. Instead of serving static files from /flaskapp/static/ I want to serve static files using /flaskapp/userdata/<user>/<current-template>/static/ directory where and will be determined dynamically at run time. How to do this?
Presumably you're using a web server in front of Flask. One way to solve this (which I generally use when using Apache + mod_wsgi for custom WSGI apps) is to just serve the directory straight from disk via the web server. On Apache, I just use an Alias directive for this.
If you want to vary the file served under a given URL per user, you would have to pipe the file through Flask. You'd have to figure out how to properly route the request; after that, you might be able to use wsgi.file_wrapper to send the correct file (though I'm not sure how you'd get at this through Flask).
This is what I use to serve files from whatever system directory.
The app searches a specific directory for a specific requested file:
when someone accesses http:/host/where/to/serve/files/example_file.txt the application will try to return example_file.txt as attachment, if the file doesn't exist it will return a 404.
You can adjust this and build the DIRECTORY_TO_SERVE_PATH with your user variables.
You should also validate the files to serve if you have any restrictions because this returns any file that exists in the path.
import os
from flask import abort, send_from_directory
DIRECTORY_TO_SERVE_PATH = '/where/files/are/in/disk/'
#app.route('/where/to/serve/files/<path:filename>')
def download_file(filename):
if os.path.exists(DIRECTORY_TO_SERVE_PATH + filename):
return send_from_directory(DIRECTORY_TO_SERVE_PATH, filename, as_attachment=True)
else:
abort(404)