Merge pull request #3391 from pallets/explain-escape

explain escape at top of quickstart
This commit is contained in:
David Lord 2019-10-12 19:03:34 -07:00 committed by GitHub
commit 4bceeccfff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -3,48 +3,59 @@
Quickstart Quickstart
========== ==========
Eager to get started? This page gives a good introduction to Flask. It Eager to get started? This page gives a good introduction to Flask.
assumes you already have Flask installed. If you do not, head over to the Follow :doc:`installation` to set up a project and install Flask first.
:ref:`installation` section.
A Minimal Application A Minimal Application
--------------------- ---------------------
A minimal Flask application looks something like this:: A minimal Flask application looks something like this:
.. code-block:: python
from flask import Flask from flask import Flask
from markupsafe import escape
app = Flask(__name__) app = Flask(__name__)
@app.route('/') @app.route("/")
def hello_world(): def hello_world():
return 'Hello, World!' return f"<p>Hello, {escape(name)}!</p>"
So what did that code do? So what did that code do?
1. First we imported the :class:`~flask.Flask` class. An instance of this 1. First we imported the :class:`~flask.Flask` class. An instance of
class will be our WSGI application. this class will be our WSGI application.
2. Next we create an instance of this class. The first argument is the name of 2. Next we create an instance of this class. The first argument is the
the application's module or package. If you are using a single module (as name of the application's module or package. ``__name__`` is a
in this example), you should use ``__name__`` because depending on if it's convenient shortcut for this that is appropriate for most cases.
started as application or imported as module the name will be different This is needed so that Flask knows where to look for resources such
(``'__main__'`` versus the actual import name). This is needed so that as templates and static files.
Flask knows where to look for templates, static files, and so on. For more 3. We then use the :meth:`~flask.Flask.route` decorator to tell Flask
information have a look at the :class:`~flask.Flask` documentation. what URL should trigger our function.
3. We then use the :meth:`~flask.Flask.route` decorator to tell Flask what URL 4. The function returns the message we want to display in the user's
should trigger our function. browser. The default content type is HTML, so HTML in the string
4. The function is given a name which is also used to generate URLs for that will be rendered by the browser.
particular function, and returns the message we want to display in the
user's browser.
Just save it as :file:`hello.py` or something similar. Make sure to not call .. note:: HTML escaping
When returning HTML (the default response type in Flask), any user
input rendered in the output must be escaped to protect from
injection attacks. HTML templates in Jinja, introduced later, will
do this automatically. :func:`~markupsafe.escape`, shown above, can
be used manually. It's omitted for brevity in the examples below.
Save it as :file:`hello.py` or something similar. Make sure to not call
your application :file:`flask.py` because this would conflict with Flask your application :file:`flask.py` because this would conflict with Flask
itself. itself.
To run the application you can either use the :command:`flask` command or To run the application, use the :command:`flask` command or
python's ``-m`` switch with Flask. Before you can do that you need :command:`python -m flask`. Before you can do that you need
to tell your terminal the application to work with by exporting the to tell your terminal the application to work with by exporting the
``FLASK_APP`` environment variable:: ``FLASK_APP`` environment variable:
.. code-block:: text
$ export FLASK_APP=hello.py $ export FLASK_APP=hello.py
$ flask run $ flask run
@ -59,12 +70,6 @@ And on PowerShell::
PS C:\path\to\app> $env:FLASK_APP = "hello.py" PS C:\path\to\app> $env:FLASK_APP = "hello.py"
Alternatively you can use :command:`python -m flask`::
$ export FLASK_APP=hello.py
$ python -m flask run
* Running on http://127.0.0.1:5000/
This launches a very simple builtin server, which is good enough for testing This launches a very simple builtin server, which is good enough for testing
but probably not what you want to use in production. For deployment options see but probably not what you want to use in production. For deployment options see
:ref:`deployment`. :ref:`deployment`.
@ -203,17 +208,17 @@ of the argument like ``<converter:variable_name>``. ::
@app.route('/user/<username>') @app.route('/user/<username>')
def show_user_profile(username): def show_user_profile(username):
# show the user profile for that user # show the user profile for that user
return 'User %s' % escape(username) return f'User {username}'
@app.route('/post/<int:post_id>') @app.route('/post/<int:post_id>')
def show_post(post_id): def show_post(post_id):
# show the post with the given id, the id is an integer # show the post with the given id, the id is an integer
return 'Post %d' % post_id return f'Post {post_id}'
@app.route('/path/<path:subpath>') @app.route('/path/<path:subpath>')
def show_subpath(subpath): def show_subpath(subpath):
# show the subpath after /path/ # show the subpath after /path/
return 'Subpath %s' % escape(subpath) return f'Subpath {subpath}'
Converter types: Converter types:
@ -281,9 +286,7 @@ Python shell. See :ref:`context-locals`.
.. code-block:: python .. code-block:: python
from flask import Flask, escape, url_for from flask import url_for
app = Flask(__name__)
@app.route('/') @app.route('/')
def index(): def index():
@ -295,7 +298,7 @@ Python shell. See :ref:`context-locals`.
@app.route('/user/<username>') @app.route('/user/<username>')
def profile(username): def profile(username):
return '{}\'s profile'.format(escape(username)) return f'{username}\'s profile'
with app.test_request_context(): with app.test_request_context():
print(url_for('index')) print(url_for('index'))
@ -416,12 +419,12 @@ Automatic escaping is enabled, so if ``name`` contains HTML it will be escaped
automatically. If you can trust a variable and you know that it will be automatically. If you can trust a variable and you know that it will be
safe HTML (for example because it came from a module that converts wiki safe HTML (for example because it came from a module that converts wiki
markup to HTML) you can mark it as safe by using the markup to HTML) you can mark it as safe by using the
:class:`~jinja2.Markup` class or by using the ``|safe`` filter in the :class:`~markupsafe.Markup` class or by using the ``|safe`` filter in the
template. Head over to the Jinja 2 documentation for more examples. template. Head over to the Jinja 2 documentation for more examples.
Here is a basic introduction to how the :class:`~jinja2.Markup` class works:: Here is a basic introduction to how the :class:`~jinja2.Markup` class works::
>>> from flask import Markup >>> from markupsafe import Markup
>>> Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>' >>> Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>'
Markup(u'<strong>Hello &lt;blink&gt;hacker&lt;/blink&gt;!</strong>') Markup(u'<strong>Hello &lt;blink&gt;hacker&lt;/blink&gt;!</strong>')
>>> Markup.escape('<blink>hacker</blink>') >>> Markup.escape('<blink>hacker</blink>')
@ -495,8 +498,6 @@ test request so that you can interact with it. Here is an example::
The other possibility is passing a whole WSGI environment to the The other possibility is passing a whole WSGI environment to the
:meth:`~flask.Flask.request_context` method:: :meth:`~flask.Flask.request_context` method::
from flask import request
with app.request_context(environ): with app.request_context(environ):
assert request.method == 'POST' assert request.method == 'POST'
@ -582,7 +583,6 @@ of the client to store the file on the server, pass it through the
:func:`~werkzeug.utils.secure_filename` function that :func:`~werkzeug.utils.secure_filename` function that
Werkzeug provides for you:: Werkzeug provides for you::
from flask import request
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
@app.route('/upload', methods=['GET', 'POST']) @app.route('/upload', methods=['GET', 'POST'])
@ -706,6 +706,8 @@ you can use the :func:`~flask.make_response` function.
Imagine you have a view like this:: Imagine you have a view like this::
from flask import render_template
@app.errorhandler(404) @app.errorhandler(404)
def not_found(error): def not_found(error):
return render_template('error.html'), 404 return render_template('error.html'), 404
@ -714,6 +716,8 @@ You just need to wrap the return expression with
:func:`~flask.make_response` and get the response object to modify it, then :func:`~flask.make_response` and get the response object to modify it, then
return it:: return it::
from flask import make_response
@app.errorhandler(404) @app.errorhandler(404)
def not_found(error): def not_found(error):
resp = make_response(render_template('error.html'), 404) resp = make_response(render_template('error.html'), 404)
@ -747,6 +751,8 @@ more complex applications.
.. code-block:: python .. code-block:: python
from flask import jsonify
@app.route("/users") @app.route("/users")
def users_api(): def users_api():
users = get_all_users() users = get_all_users()
@ -768,9 +774,7 @@ unless they know the secret key used for signing.
In order to use sessions you have to set a secret key. Here is how In order to use sessions you have to set a secret key. Here is how
sessions work:: sessions work::
from flask import Flask, session, redirect, url_for, escape, request from flask import session
app = Flask(__name__)
# Set the secret key to some random bytes. Keep this really secret! # Set the secret key to some random bytes. Keep this really secret!
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/' app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'
@ -778,7 +782,7 @@ sessions work::
@app.route('/') @app.route('/')
def index(): def index():
if 'username' in session: if 'username' in session:
return 'Logged in as %s' % escape(session['username']) return f'Logged in as {session["username"]}'
return 'You are not logged in' return 'You are not logged in'
@app.route('/login', methods=['GET', 'POST']) @app.route('/login', methods=['GET', 'POST'])
@ -799,9 +803,6 @@ sessions work::
session.pop('username', None) session.pop('username', None)
return redirect(url_for('index')) return redirect(url_for('index'))
The :func:`~flask.escape` mentioned here does escaping for you if you are
not using the template engine (as in this example).
.. admonition:: How to generate good secret keys .. admonition:: How to generate good secret keys
A secret key should be as random as possible. Your operating system has A secret key should be as random as possible. Your operating system has