Flask本身相當于一個內核,其他幾乎所有的功能都要用到擴展(郵件擴展Flask-Mail,用戶認證Flask-Login),都需要用第三方的擴展來實現。
Flask對WSGI (路由)的實現,是采用 Werkzeug,而模板引擎(業務視圖) 則使用 Jinja2。這兩個是Flask框架的核心。
Flask核心就是作為一個Webapp框架的兩個基礎部分:
- CGI (WSGI) 路由:是由Werkzeug實現,即將網絡請求翻譯成Python語言
- Template 業務模版:是由Python流行的Jinja2實現
除此之外,Flask其它一切的都是由第三方插件實現:
包括:
Flask-SQLalchemy:操作數據庫; Flask-migrate:管理遷移數據庫; Flask-Mail:郵件; Flask-WTF:表單; Flask-Bable:提供國際化和本地化支持,翻譯; Flask-script:插入腳本; Flask-Login:認證用戶狀態; Flask-OpenID:認證; Flask-RESTful:開發REST API的工具; Flask-Bootstrap:集成前端Twitter Bootstrap框架; Flask-Moment:本地化日期和時間; Flask-Admin:簡單而可擴展的管理接口的框架
參考Flask擴展列表:http://flask.pocoo.org/extensions/
Flask安裝
安裝很簡單:
$ pip install flask
建議在Virtualenv虛擬環境下安裝,因為Flask需要一系列的依賴,最好給Flask生成一個專用的生產環境,并生產requirement.txt
依賴列表:
# 生產虛擬環境
$ virtualenv ./flask_env
# 啟動虛擬環境
$ ./flask_env/bin/active
# 生產依賴列表
$ pip freeze > requirements.txt
Hello World
一個最簡單的Flask程序,只需要三步:
- 生成Flask類的實例,即一個APP
- 指定目錄的
路由規則
,即不同路徑可以實現不同的操作。 - 運行app:
app.run()
,hello-world.py
:
from flask import Flask
# 生產一個Flask APP 實例,并指向當前文件(模塊)
app = Flask(__name__)
# 指定"/"根目錄的路由規則
@app.route('/')
def index():
return 'Hello World'
# 開始運行app
app.run()
Flask中,路由的實現是用裝飾器:@app.route("/")
這種方式來做到的。
Flask路由規則
頁面顯示指定內容:
@app.route('/')
def index():
return '<h1> 歡迎訪問此頁面 </h1>'
頁面返回指定內容、狀態碼、headers等:
@app.route('/')
def index():
body = '<h1> 歡迎訪問此頁面 </h1>'
status_code = 200
headers = {'Cache-Control':'no-cache', 'Connection':'keep-alive'}
return body, status_code, headers
指定接收的請求方式
:
@app.route('/', method='GET')
# ...
給路由傳參數:
@app.route('/orders/<order_id>')
def hello_itheima(order_id):
return 'The ID of order is: %d' % order_id
限制路由參數的數據類型:
@app.route('/orders/<int:order_id>')
# ...
其中int:order_id
是指定參數order_id
必須能轉換成int整數,否則這個請求就會自動被拒絕。
比如用戶請求http://xyz.com/orders/HAHAHAH
,這就不成功。而http://xyz.com/orders/210
這樣的就成功。

中斷請求:
# ...
from flask import abort
@app.route('/wrong-page')
def hello():
# 中斷請求,并返回404狀態碼
abort(404)
處理錯誤請求:
# ...
@app.errorhandler(404)
def handel_404_error():
return '<h1> The page does not exist </h1>'
其中,如果路由中調用了abort(404)
中斷函數,或是其它產生404錯誤的方法,這個app.errorhandler
都會被自動調用。
返回“渲染”過的模版(即把動態的模版渲染成靜態的HTML):
#...
from flask import render_template
@app.route('/')
def index():
return render_template('index.html')
Flask 路由分割
如果所有路徑的路由都定義在一個文件里,會很難維護,所以必定要把各個路徑的路由分拆到不同的文件里。
這種分拆很簡單:
正常情況下,在index.py
主模塊中,我們還是一樣正常的定義路由:
#...
@app.route('/')
def index():
pass
然后我們可以把其它路由的處理函數分別放在別的文件里,比如:
register.py
中定義“普通函數”:
def reg():
pass
以及login.py
中定義一個普通函數:
def signin():
pass
然后回到主模塊index.py
中,我們可以導入這些函數,并顯式的將這些函數注冊到路由上:
from flask import Flask
from register import reg
from login import signin
app = Flask(__name__)
app.route('/register')(reg)
app.route('/login')(signin)
@app.route('/')
def index():
pass
然后我們可以用app.url_map
獲得當前定義過的所有路由:
print( app.url_map )

Flask Blueprint 藍圖
我們手動分割路由處理函數,然后分別導入,這樣雖然也簡單,但是不不好的地方是,主模塊外定義的各個處理函數,本身很難看出來處理的是什么路由邏輯。
為此,Flask提供了另一種路由分割
的方法:即Blueprint
類。
而這個Blueprint類生成的對象,是在子模塊中代替了之前我們所使用的Flask
類生成的app對象。
也就是說:主模塊還是用app,但是子模塊中用藍圖blueprint。
假設我們現在有一個子模塊order.py
定義"/order"
路徑的路由,那么文件中定義如下:
from flask import Blueprint
# 生成藍圖實例:參數中一個是藍圖名稱,一個是主模塊名稱
app_orders = Bluepint('blueprint_orders', __name__)
# 將路由添加到藍圖里
@app_orders.route('/orders')
def get_orders():
pass
然后回到主模塊index.py
中,把藍圖注冊到主路由上:
#...
from orders import app_orders
app = Flask(__name__)
app.register_blueprint( app_orders )
#...
Hooks 鉤子事件
Flask提供一個完整請求至回應的事件流,其中包括:
-
@app.before_first_request
: 接受第一次請求之前執行 -
@app.before_request
: 接受請求前,每次請求之前都執行。 -
@app.route()
: 處理請求 -
@app.after_request
: 請求之后執行,但前提是請求中沒有出現異常
-@app.teardown_request
: 關閉請求時,即每次請求是否異常都會被執行
以下是鉤子的用法:
#...
@app.before_first_request
def handle_before_first_request():
pass
@app.route('/')
def index():
pass
@app....
def ...
Flask上下文請求對象 flask.current_app
request.current_app
是Flask特有的一種request請求處理
方式,不同于flask.request
對象的處理方式,它是能區分多個請求的。
在我們常用的flask.request
對象中,會有一個很嚴重的問題:即它是一個全局變量
。也就是說,如果服務器在處理并發請求時使用的是在同一個進程里的多線程,那么不同用戶的請求也許會使用同一個flask.request
對象!這時候request中的請求信息就會出現混淆!
所以Flask引入了request.current_app
這個對象,即它能夠根據上下文來區分不同人的請求。
這是怎么做到的呢?其實很簡單,它只是把request變為一個局部變量
而已。這樣一來,每次的request請求,都是各自獨立的局部對象。
返回響應信息 flask.make_response
除了我們自己定義返回的信息外,Flask提供了一個內置的make_response
對象,便于處理返回信息。
返回全文信息:
from flask import make_response
@app.route('/')
def index():
resp = make_response('<h1> 歡迎訪問此頁面 </h1>')
resp.status = 200
resp.headers['Cache-Control'] = 'no-cache'
return resp
設置cookies:
from flask import make_response
@app.route('/')
def index():
resp = make_response('<h1> 此頁面會設置你的cookies :) </h1>')
resp.set_cookie('uuid', '1230sfjdlsj3uu')
resp.set_cookie('name', 'Jason', max_age=360)
return resp
其中,max_age
是cookie的存活時間,以s秒為單位。不設置的話,默認是臨時cookies
,即瀏覽器關閉后立馬失效。
刪除cookie:resp.delete_cookie('uuid')
。注意,這里的刪除并不是立馬刪除瀏覽器中用戶的cookie,而只是把max_age
設置為0,即瀏覽器關閉后立馬失效。
獲取請求信息 flask.request
Flask中有一個request對象,接收了一切對當前模塊的請求數據。
使用的話,直接在@app.route
后面的函數中用def index(request)
接收來自裝飾器的請求對象即可使用。
request參數類型:

常用的各種類型操作如下:
from flask import request
app = Flask(__name__)
@app.route('/', method='POST')
def index(request):
# Get uploaded file
afile = request.files.get('pic')
with open('./pic.jpg', 'w') as f:
f.write( afile.read() )
# Get a form
form = request.from # Dict類型
name = form.get('name')
age = form.get('age')
# Get cookies
uuid = request.cookies.get('uuid')
name = request.cookies.get('name')
會話處理 flask.session
在登錄頁設置session,并在index頁根據session判斷是否登錄:
from flask import session
#...
app.config['SECRET_KEY'] = 'asdlkjflaj23jrsdjf任意字符串作為密鑰kaljdsl;fkja;j'
@app.route('/login')
def login():
# 設置sessions
session['uuid'] = '123abadsf'
return '<p> 登錄成功 </p>'
@app.route('/')
def index():
# 獲取sessions
uuid = session['uuid']
# 判別session是否存在
if uuid:
return '<p> 之前已登錄過 </p>'
else:
return '<p> 未登錄,請重新登錄 </p>'
其中,Flask默認情況下,會利用app.config['SECRET_KEY']
的值作為一個密鑰,來加密你手動設置的session,然后把這個信息轉換為名叫session
的cookie存在瀏覽器中。
這個是Flask特別的一點。
但是把敏感的session數據保存到誰都能訪問的cookie中,即使加密了也不是很安全。
所以一般我們還是會手動把session數據存到服務器后臺的數據庫中,而不是存到cookie中。
每次驗證再與數據庫進行對比。
表單處理 request.form
動態網頁必須要的就是Form表單。Flask中有自帶的form表單處理方法。不過我們也可以用第三方插件Flask-WTF
實現。
這里我們先只講自帶的處理方式。
Flask自帶表單處理
假設我們有一個表單模版form.html
:
<form method="post">
用戶名:<input type="text" name="username">
密碼: <input type="password" name="password">
確認密碼: <input type="password" name="password2">
<input type="submit" value="提交"><br>
{% for message in get_flashed_messages() %}
{{ message }}
{% endfor %}
</form>
當用戶點擊submit提交時,
整個form信息就會用POST方式提交到Flask的路由文件abc.py
中。
我們進行處理如下:
from flask import Flask
from flask import render_template
from flask import request
app.secret_key = 'abc123'
@app.route('/', methods=['GET', 'POST'])
def hello():
if request.method == 'POST':
# 獲取參數, 并效驗參數完整性, 如果有問題就進行flash
username = request.form.get('username')
password = request.form.get('password')
password2 = request.form.get('password2')
if not all([username, password, password2]):
flash('params error')
elif password != password2:
flash('password error')
else:
print username
return 'success'
return render_template('Congratulations.html')
Flask的HTTP Server
一般我們在開發調試過程中,可以用Flask自帶的WSGI和一個小HTTP Server來實現整個App正常運轉。
但是生產環境中,這兩個自帶的組件就效率很低了。所以我們需要用效率更高的獨立的CGI和獨立的HTTP Server服務器來部署真正的生產環境
一般常見的選項有:
- HTTP Server -> 首推Nginx
- CGI翻譯器 -> Gunicorn (Python開發,實現了WSGI翻譯)
所以,我們一般采用Nginx + Gunicorn + Flask
來部署網絡應用。
Gunicorn的使用:
# 安裝
$ pip install gunicorn
# 進入Flask app的主目錄
cd ./myFlask
# 用gunicorn服務器啟動Flask app
$ gunicorn -w 4 -b 127.0.0.1:8080 main:app
這個時候,flask就在gunicorn的HTTP服務器上運行了,可以通過127.0.0.1:8080訪問到app。