from flask import Flask
= Flask(__name__)
app
@app.route('/')
def hello():
return "Hello World!"
if __name__ == '__main__':
app.run()
After executing this program, a built-in web server of flask will be actived in 127.0.0.1:5000
, which includes that how “micro” the framework is.
Now let’s check the structure of the program. First we create a Flask
object app
, which uses the __name__
of program as parameter(why?), then @route
decorator tell creates the mapping between path and the content. In this case, the root path is mapped to the words “Hello World!”. After these preparation are done. We use app.run()
method to actually makes the WSGI (Web Server Gateway Interface) application alive.
Besides writing in the code, We can use the CLI tool (like bash) to run the object like this:
# bash
FLASK_APP="app.py"
flask run
Some other configuration can also be set through this way:
# bash
FLASK_ENV="development"
FLASK_DEBUG=0
ENV
tells the application to deploy in development or production mode, different authorization are applied in two mode, if and only if the mode at dev status, DEBUG
can be activated.
By default the ip address and port are set as 127.0.0.1:5000
, the localhost which is not accessible to the network, to make it accessible we can manually set it to0.0.0.0
by --host
parameter:
# bash
flask run --host=0.0.0.0
The fact is run
method (no matter code way or CLI way) is not the standard method and usually deprecated in production because the bad proformance. When actually deployed in server side, the common way is use nginx
, gunicorn
, or some other web solutions.
Decorator is used to create the create route mapping, parameter in the path can be injected through <var>
format:
@app.route('/user/<username>')
def user(username):
return "hello, {username}".format(username=username)
The bottom mechanism about decorator is explained at Python Cookbook.
And more specific, you can control the type of parameter:
@app.route('/user/<string:username>')
@app.route('/number/<int:number>')
The type includes string
, path
, int
, float
, and uuid
.
By default only GET
method is allowed to a path, the allowed methods can be set as another decorator parameter:
from flask import request
@app.route('/method', methods=['GET', 'POST', 'PUT'])
def method(){
# reqeust object stores method info
return "This is a {method} message.".format(method=request.method)
}
e.g. the login path usually both shows the login form and receives the form post, this is usually constructed as:
@app.route('/login', methods=['GET', 'POST'])
def login(){
if request.method == 'GET':
return login_check()
else: # Flask will block any other request method besides allowed.
return show_the_login_form()
}
The parameters passed by POST
and GET
are also stored in the request
object as request.form
and request.args
dictionary respectively:
@app.route('/login', method=['GET', 'POST'])
def login():
= request.form if request.method=='POST' else request.args
payload = login_check(payload['username'], payload['password'])
is_valid return user_page() if valid else "access denied"
# This is only for explaination purpose, in practice we usually not process the GET parameters and POST form as the same...
Use the render_template
method to render page from template instead of build the html
page manually, the template file stores in folder templates
.
<!-- user.html -->
<!doctype html>
<title>user page</title>
{% if username %}
Hello, {{ username }}!
{% else %}
Hello, World!
{% endif %}
@app.route('/user/<username>')
def user(username):
return render_template('user.html', username=username)
For the most simple case we just return a string (render_templates
also just render a page content to a string, you can check its type to confirm this). While the response is more than just contents, as the request, it also includes the header, and status code, so we need a response
object instead of only built-in string. In the simple returned string case, flask converts the string into response
object automatically, and add 200
status with no header.
Once further control is required, you can create a response object manually:
from flask import make_response
@app.route('/user')
def user():
= make_response(render_template('user.html'), 200, {'header-x': 1})
resp 'header-y'] = 2
resp.headers[return resp
The behavior about request to ending slash is a little tricky, basically, you can achieve a “folder” by consider it as a “file”, which means you can get a slash-end url by accessing it without ending slash, at this time a 308 redirection appeared. The reverse approach is not allowed.
(If two kinds of url are both supported this is never happened)
@app.route('/slash/')
def slash():
return "this is a slash page!"
# http://localhost:5000/slash
127.0.0.1 - - [19/May/2019 12:42:05] "GET /slash HTTP/1.1" 308 -
127.0.0.1 - - [19/May/2019 12:42:05] "GET /slash/ HTTP/1.1" 200 -
# http://localhost:5000/slash/
127.0.0.1 - - [19/May/2019 12:56:06] "GET /slash/ HTTP/1.1" 200 -
If you try to access a unslash path with slash, 404 Not Found
is waitting for you:
@app.route('/slash')
def unslash():
return "this is a not slash page!"
redirect
methods are required to do the redirection.
from flask import redirect
@app.route('/')
def index():
return "This is the index page!"
@app.route('/re')
def re():
return redirect('/')
Usually the redirect
method is used together with url_for
method, which maps the function name and parameters to url.
Use the url_for
function to retrive a url from the function name. For example, if we have a route
@app.route('/user/<username>')
def profile(username):
return '{}\'s profile'.format(escape(username))
To find out which URL is for the handler function profile
with parameter John Doe
, we use
'profile', username='John Doe') # /user/John%20Doe url_for(
As noticed, the escape
To create url for static files, the special filename
parameter is required:
'static', filename="style.css") url_for(
You can get/set cookies from request/response object:
# Setter
@app.route('/set-name')
def set_name():
= make_response('set name sucessful!')
resp 'name', 'Robert')
resp.set_cookie(return resp
# Getter
@app.route('/get-name')
def get_name():
= request.cookies.get('name')
name return "You name is %s!" % name
Session is bulit on the cookies mechanism with crypto technique appied to identify the user securely. The info you want to store will be encrypted to a string and stored at one cookie item named session, so the infomation can be stored in user side to show its identification but only server knows its content.
from flask import escape, url_for, redirect
# session mechanism don't rely on asymmetric entryption. Only one key needed.
# this is a 16 bytes key, os.urandom(16) can be a good generator.
= b'_5#y2L"F4Q8z\n\xec]/'
app.secret_key
@app.route('/')
def index():
if 'username' in session:
= escape(session['username'])
username return "You're logged in as %s" % username
else:
return "You're not logged in!"
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
= """
login_form <form method="post">
<input type="text" name="username" />
<input type="submit" value="login!" />
</form>
"""
return login_form
if request.method == 'POST'
'username'] = request.form['username']
session[return redirect(url_for('index'))
@app.route('/logout')
def logout():
'username', None)
session.pop(return redirect(url_for('index'))
In this example, the /
page indicates wheather the username is set. /login
and /logout
page set the remove username in session respectively.
You should able to understand this through the following demo:
@app.route('/')
def index():
return "hello, you're in the index page!"
@app.route('/upload', methods=['GET', 'POST'])
def upload():
if request.method == 'GET':
= """
form_page <form method="post" enctype="multipart/form-data">
<input type="file" name="the_file" />
<input type="submit" />
</form>
"""
return form_page
if request.method == 'POST':
= requests.files['the_file']
f '/User/rex/hello.txt')
f.save(return redirect('/')
Notices that the <from>
element should has attribuate enctype="multipart/form-data"
to make the browser support file transfer, otherwise no files will be transfer to the server.
You can manually abort
the page to any kinds of error, and handle any kinds of errors by errorhandler
decorator.
from flask import abort
@app.route('/notexist')
def notexist():
404)
abort(
@app.errorhandler(404)
def not_found(error):
print(error) # This stores the error info in code-specific class
# for 404, it's <class 'werkzeug.exceptions.NotFound'>
return "NOT FOUND", 404