Flask是由python實現(xiàn)的一個web微框架,讓我們可以使用Python語言快速實現(xiàn)一個網(wǎng)站或Web服務。而且有對應的python3及python2版本。首先這邊選擇的是python3.6,雖然python3在網(wǎng)上好像名聲不咋地,而且一度有文章說python3正在毀滅Python,但是反正是別人選的,也就將就了。
在網(wǎng)上看別人下載個flask很麻煩,反正我的很簡單,windows環(huán)境下的
1.安裝Flask
pip install flask
2.目錄結(jié)構(gòu)
通過別人的目錄大致了解一下flask框架的目錄結(jié)構(gòu)。
flask-demo/
├ run.py # 應用啟動程序
├ config.py # 環(huán)境配置
├ requirements.txt # 列出應用程序依賴的所有Python包
├ tests/ # 測試代碼包
│ ├ __init__.py
│ └ test_*.py # 測試用例
└ myapp/
├ admin/ # 藍圖目錄
├ static/
│ ├ css/ # css文件目錄
│ ├ img/ # 圖片文件目錄
│ └ js/ # js文件目錄
├ templates/ # 模板文件目錄
├ __init__.py
├ forms.py # 存放所有表單,如果多,將其變?yōu)橐粋€包
├ models.py # 存放所有數(shù)據(jù)模型,如果多,將其變?yōu)橐粋€包
└ views.py # 存放所有視圖函數(shù),如果多,將其變?yōu)橐粋€包
3.開始 Hello world
最簡單的測試
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World'
if __name__ == '__main__':
app.debug = True # 設置調(diào)試模式,生產(chǎn)模式的時候要關掉debug
app.run()
這是flask框架制作的一個最小的應用。使用python運行后訪問localhost:5000就能看到網(wǎng)頁顯示Hello world。
這里首先引入了Flask類,然后給這個類創(chuàng)建了一個實例,name代表這個模塊的名字。因為這個模塊是直接被運行的所以此時name的值是main。然后用route()這個修飾器定義了一個路由,告訴flask如何訪問該函數(shù)。最后用run()函數(shù)使這個應用在服務器上運行起來。
4.模板
Flask的模板功能是基于Jinja2模板引擎實現(xiàn)的。讓我們來實現(xiàn)一個例子吧。創(chuàng)建一個新的Flask運行文件(你應該不會忘了怎么寫吧),代碼如下:
from flask import Flask
from flask import render_template
app = Flask(__name__)
@app.route('/hello')
@app.route('/hello/<name>')
def hello(name=None):
return render_template('hello.html', name=name)
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)
這段代碼”hello()”函數(shù)并不是直接返回字符串,而是調(diào)用了”render_template()”方法來渲染模板。方法的第一個參數(shù)”hello.html”指向你想渲染的模板名稱,第二個參數(shù)”name”是你要傳到模板去的變量,變量可以傳多個。
那么這個模板”hello.html”在哪兒呢,變量參數(shù)又該怎么用呢?接下來我們創(chuàng)建模板文件。在當前目錄下,創(chuàng)建一個子目錄”templates”(注意,一定要使用這個名字)。然后在”templates”目錄下創(chuàng)建文件”hello.html”,內(nèi)容如下:
<!doctype html>
<title>Hello Sample</title>
{% if name %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello World!</h1>
{% endif %}
這段代碼是不是很像HTML?接觸過其他模板引擎的朋友們肯定立馬秒懂了這段代碼。它就是一個HTML模板,根據(jù)”name”變量的值,顯示不同的內(nèi)容。變量或表達式由”{{ }}”修飾,而控制語句由”{% %}”修飾,其他的代碼,就是我們常見的HTML。
讓我們打開瀏覽器,輸入”http://localhost:5000/hello/man”,頁面上即顯示大標題”Hello man!”。我們再看下頁面源代碼
<!doctype html>
<title>Hello from Flask</title>
<h1>Hello man!</h1>
果然,模板代碼進入了”Hello {{ name }}!”分支,而且變量”{{ name }}”被替換為了”man”。Jinja2的模板引擎還有更多強大的功能,包括for循環(huán),過濾器等。模板里也可以直接訪問內(nèi)置對象如request, session等。對于Jinja2的細節(jié),感興趣的朋友們可以自己去查查。
5.模板繼承
一般我們的網(wǎng)站雖然頁面多,但是很多部分是重用的,比如頁首,頁腳,導航欄之類的。對于每個頁面,都要寫這些代碼,很麻煩。Flask的Jinja2模板支持模板繼承功能,省去了這些重復代碼。讓我們基于上面的例子,在”templates”目錄下,創(chuàng)建一個名為”layout.html”的模板:
<!doctype html>
<title>Hello Sample</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
<div class="page">
{% block body %}
{% endblock %}
</div>
再修改之前的”hello.html”,把原來的代碼定義在”block body”中,并在代碼一開始”繼承”上面的”layout.html”:
{% extends "layout.html" %}
{% block body %}
{% if name %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello World!</h1>
{% endif %}
{% endblock %}
打開瀏覽器,再看下”http://localhost:5000/hello/man”頁面的源碼。
<!doctype html>
<title>Hello Sample</title>
<link rel="stylesheet" type="text/css" href="/static/style.css">
<div class="page">
<h1>Hello man!</h1>
</div>
你會發(fā)現(xiàn),雖然”render_template()”加載了”hello.html”模板,但是”layout.html”的內(nèi)容也一起被加載了。而且”hello.html”中的內(nèi)容被放置在”layout.html”中”{% block body %}”的位置上。形象的說,就是”hello.html”繼承了”layout.html”。
6. HTML自動轉(zhuǎn)義
我們看下下面的代碼:
@app.route('/')
def index():
return '<div>Hello %s</div>' % '<em>Flask</em>'
打開頁面,你會看到”Hello Flask”字樣,而且”Flask”是斜體的,因為我們加了”em”標簽。但有時我們并不想讓這些HTML標簽自動轉(zhuǎn)義,特別是傳遞表單參數(shù)時,很容易導致HTML注入的漏洞。我們把上面的代碼改下,引入”Markup”類:
from flask import Flask, Markup
app = Flask(__name__)
@app.route('/')
def index():
return Markup('<div>Hello %s</div>') % '<em>Flask</em>'
再次打開頁面,”em”標簽顯示在頁面上了。Markup還有很多方法,比如”escape()”呈現(xiàn)HTML標簽, “striptags()”去除HTML標簽。這里就不一一列舉了。
7.Request 對象
from flask import Flask,url_for,request,render_template
@app.route('/login', methods=['POST', 'GET'])
def login():
if request.method == 'POST':
if request.form['user'] == 'admin':
return 'Admin login successfully!'
else:
return 'No such user!'
title = request.args.get('title', 'Default')
return render_template('login.html', title=title)
if __name__ == "__main__":
app.run(debug=True)
簡單解釋下,request中”method”變量可以獲取當前請求的方法,即”GET”, “POST”, “DELETE”, “PUT”等;”form”變量是一個字典,可以獲取Post請求表單中的內(nèi)容,在上例中,如果提交的表單中不存在”user”項,則會返回一個”KeyError”,你可以不捕獲,頁面會返回400錯誤(想避免拋出這”KeyError”,你可以用request.form.get(“user”)來替代)。而”request.args.get()”方法則可以獲取Get請求URL中的參數(shù),該函數(shù)的第二個參數(shù)是默認值,當URL參數(shù)不存在時,則返回默認值。
在當前目錄下,創(chuàng)建一個子目錄”templates”(注意,一定要使用這個名字)。然后在”templates”目錄下,添加
layout.html
<!doctype html>
<title>Hello Sample</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
<div class="page">
{% block body %}
{% endblock %}
</div>
login.html
{% extends "layout.html" %}
{% block body %}
<form name="login" action="/login" method="post">
Hello {{ title }}, please login by:
<input type="text" name="user" />
</form>
{% endblock %}
8.會話session
會話可以用來保存當前請求的一些狀態(tài),以便于在請求之前共享信息。
from flask import Flask,url_for,request,render_template,redirect,session
@app.route('/login', methods=['POST', 'GET'])
def login():
if request.method == 'POST':
if request.form['user'] == 'admin':
session['user'] = request.form['user']
return 'Admin login successfully!'
else:
return 'No such user!'
if 'user' in session:
return 'Hello %s!' % session['user']
else:
title = request.args.get('title', 'Default')
return render_template('login.html', title=title)
@app.route('/logout')
def logout():
session.pop('user', None)
return redirect(url_for('login'))
app.secret_key = '123456'
if __name__ == "__main__":
app.run(debug=True)
你可以看到,”admin”登陸成功后,再打開”login”頁面就不會出現(xiàn)表單了。然后打開logout頁面可以登出。session對象的操作就跟一個字典一樣。特別提醒,使用session時一定要設置一個密鑰”app.secret_key”,如上例。不然你會得到一個運行時錯誤,內(nèi)容大致是”RuntimeError: the session is unavailable because no secret key was set”。密鑰要盡量復雜,最好使用一個隨機數(shù),這樣不會有重復,上面的例子不是一個好密鑰。
9.構(gòu)建響應
在之前的例子中,請求的響應我們都是直接返回字符串內(nèi)容,或者通過模板來構(gòu)建響應內(nèi)容然后返回。其實我們也可以先構(gòu)建響應對象,設置一些參數(shù)(比如響應頭)后,再將其返回。修改下上例中的Get請求部分:
from flask import Flask,url_for,request,render_template,redirect,session,make_response
@app.route('/login', methods=['POST', 'GET'])
def login():
if request.method == 'POST':
...
if 'user' in session:
...
else:
title = request.args.get('title', 'Default')
response = make_response(render_template('login.html', title=title), 200)
response.headers['key'] = 'value'
return response
if __name__ == "__main__":
app.run(debug=True)
打開瀏覽器調(diào)試,在Get請求用戶未登錄狀態(tài)下,你會看到響應頭中有一個”key”項?!眒ake_response”方法就是用來構(gòu)建response對象的,第二個參數(shù)代表響應狀態(tài)碼,缺省就是200。
10.Cookie的使用
提到了Session,當然也要介紹Cookie嘍,畢竟沒有Cookie,Session就根本沒法用(不知道為什么?查查去)。Flask中使用Cookie也很簡單:
from flask import Flask,url_for,request,render_template,redirect,session,make_response
import time
@app.route('/login', methods=['POST', 'GET'])
def login():
response = None
if request.method == 'POST':
if request.form['user'] == 'admin':
session['user'] = request.form['user']
response = make_response('Admin login successfully!')
response.set_cookie('login_time', time.strftime('%Y-%m-%d %H:%M:%S'))
...
else:
if 'user' in session:
login_time = request.cookies.get('login_time')
response = make_response('Hello %s, you logged in on %s' % (session['user'], login_time))
...
return response
app.secret_key = '123456'
if __name__ == "__main__":
app.run(debug=True)
這次我們引入了”time”模塊來獲取當前系統(tǒng)時間。我們在返回響應時,通過”response.set_cookie()”函數(shù),來設置Cookie項,之后這個項值會被保存在瀏覽器中。這個函數(shù)的第三個參數(shù)(max_age)可以設置該Cookie項的有效期,單位是秒,不設的話,在瀏覽器關閉后,該Cookie項即失效。
在請求中,”request.cookies”對象就是一個保存了瀏覽器Cookie的字典,使用其”get()”函數(shù)就可以獲取相應的鍵值。
11.錯誤處理
使用”abort()”函數(shù)可以直接退出請求,返回錯誤代碼:
rom flask import Flask,abort
app = Flask(__name__)
@app.route('/error')
def error():
abort(404)
if __name__ == "__main__":
app.run(debug=True)
上例會顯示瀏覽器的404錯誤頁面。有時候,我們想要在遇到特定錯誤代碼時做些事情,或者重寫錯誤頁面,可以用下面的方法:
rom flask import Flask,abort
app = Flask(__name__)
@app.errorhandler(404)
def page_not_found(error):
return render_template('404.html'), 404
if __name__ == "__main__":
app.run(debug=True)
不過,在實際開發(fā)過程中,我們并不會經(jīng)常使用”abort()”來退出,常用的錯誤處理方法一般都是異常的拋出或捕獲。裝飾器”@app.errorhandler()”除了可以注冊錯誤代碼外,還可以注冊指定的異常類型。讓我們來自定義一個異常:
class InvalidUsage(Exception):
status_code = 400
def __init__(self, message, status_code=400):
Exception.__init__(self)
self.message = message
self.status_code = status_code
@app.errorhandler(InvalidUsage)
def invalid_usage(error):
response = make_response(error.message)
response.status_code = error.status_code
return response
@app.route('/exception')
def exception():
raise InvalidUsage('No privilege to access the resource', status_code=403)
if __name__ == "__main__":
app.run(debug=True)
我們在上面的代碼中定義了一個異?!盜nvalidUsage”,同時我們通過裝飾器”@app.errorhandler()”修飾了函數(shù)”invalid_usage()”,裝飾器中注冊了我們剛定義的異常類。這也就意味著,一但遇到”InvalidUsage”異常被拋出,這個”invalid_usage()”函數(shù)就會被調(diào)用。
12.url重定向
Flask的URL規(guī)則是基于Werkzeug的路由模塊。模塊背后的思想是基于 Apache 以及更早的 HTTP 服務器主張的先例,保證優(yōu)雅且唯一的 URL。
@app.route('/projects/')
def projects():
return 'The project page'
@app.route('/about')
def about():
return 'The about page'
訪問第一個路由不帶/時,F(xiàn)lask會自動重定向到正確地址。
訪問第二個路由時末尾帶上/后Flask會直接報404 NOT FOUND錯誤。
重定向”redirect()”函數(shù)的使用在上面例子中已有出現(xiàn)。作用就是當客戶端瀏覽某個網(wǎng)址時,將其導向到另一個網(wǎng)址。常見的例子,比如用戶在未登錄時瀏覽某個需授權(quán)的頁面,我們將其重定向到登錄頁要求其登錄先。
from flask import session, redirect
@app.route('/')
def index():
if 'user' in session:
return 'Hello %s!' % session['user']
else:
return redirect(url_for('login'), 302)
“redirect()”的第二個參數(shù)時HTTP狀態(tài)碼,可取的值有301, 302, 303, 305和307,默認即302(為什么沒有304?留給大家去思考)。
轉(zhuǎn)載來源:思誠之道