Python框架學習之Flask中的視圖及路由
在前面一講中我們學習如何創建一個簡單的Flask項目,並做了一些簡單的分析。接下來在這一節中就主要來講講Flask中最核心的內容之一:Werkzeug工具箱。Werkzeug是一個遵循WSGI協議的Python函數庫。WSGI協議在前面的文章中也有提到(點我查看)。那Werkzeug有什麼作用呢?它其實實現了很多底層的東西,如Request、Response和集成URL請求路由等。
一、Werkzeug的組成:
二、routing模塊
routing模塊的主要目的是負責實現URL解析。不同的URL能夠對應不同的視圖,從而得到不同的響應信息。
1.Rule類:class Rule(RuleFactory):
用來構造不同的url模式的對象。
1 if not string.startswith('/'):
2 raise ValueError('urls must start with a leading slash')
這個判斷語句是Rule中的,表示url必須是以"/"開頭的字符串!
2.Map類:
用來存儲所有的URL規則和一些配置參數。
3.MapAdapter類
負責讓url與視圖建立關聯。
4.BaseConverter
這是一個轉換器基類,Flask中6個自帶的轉換器都是繼承於它!當然了,如果我們想要自定義一個路由匹配規則的話,也需要通過繼承它來實現。如自定義一個正則路由匹配。
流程:
(1). 導入BaseConverter類。在Flask中,所有 的路由的匹配規則都是使用轉換器對象進行記錄的。
(2). 自定義轉換器。
(3). 把自定義轉換器添加到默認轉換器的字典當中去。
(4). 使用自定義轉換器實現自定義匹配規則。
from flask import Flask
from werkzeug.routing import BaseConverter
app = Flask(__name__)
class RegexConverter(BaseConverter):
"""自定義一個正則轉換器"""
def __init__(self, map, *args):
super(RegexConverter, self).__init__(map)
# 將url中第一個參數當做匹配規則進行保存
self.regex = args[0]
print(map)
print(args[0])
# 把自定義轉換器添加到默認轉換器的字典中,並將轉換器命名爲re
app.url_map.converters['re'] = RegexConverter
# print(app.url_map.converters)
# 定義一個正則的路由規則,匹配3個數字
@app.route('/regex/<re("[0-9]{3}"):u_id>')
def index(u_id):
return "user_id爲%s" % u_id
if __name__ == "__main__":
print(app.url_map)
app.run(debug=True)
三、Request對象
Request是Flask中表示當前請求的request對象,一般稱之爲請求上下文變量(可以理解成全局變量,在任意一個視圖中都可以訪問到)。常用的屬性有:
args:url中的查詢字符串。本質上是鍵值對,跟在"?"後面,多個鍵值對之間使用"&"連接;
form:請求url中的表單數據。也是由鍵值對組成,一般使用post方法提交數據
cookies:請求中的cookie信息。由服務器生成的鍵值對,把值發送給客戶端並保存,稍後重點講解!
files:上傳的文件
1 from flask import Flask, request
2
3
4 app = Flask(__name__)
5
6
7 # 127.0.0.1:5000/?a=4&b=5
8 @app.route('/')
9 def index():
10 a = request.args.get('a')
11 b = request.args.get('b')
12
13 print('a:%s, b:%s' % (a, b))
14 return '請求成功!!'
15
16
17 if __name__ == "__main__":
18 print(app.url_map)
19 app.run(debug=True)
四、狀態保持
在分析Request對象的時候提到過Cookie,什麼是Cookie?Cookie有什麼用?以及如何通過Cookie及Session來實現狀態保持呢?
(1). 爲什麼要有狀態保持的功能?
因爲HTTP是一種無狀態協議,也就是說當用戶每一次請求時,都是一個新的請求,服務器根本不知道這個用戶做過什麼。比如當你在某個網站上想要買個東西,但是你每做一步都需要進行登錄直到下單成功,這樣對用戶來說就顯得特別麻煩,用戶體驗就特別差。如果我們的網站可以記錄該用戶的一些信息,那麼用戶就不需要每次都進行登錄了,用戶體驗就大大提高了。
(2).Cookie是什麼?
Cookie是由鍵值對組成的字符串,爲了辨別用戶身份,進行會話跟蹤而保存在本地的數據。
Cookie是服務器生成的,發送給客戶端瀏覽器,瀏覽器將以鍵值對的形式保存着,當下一次請求同一網站的時候會攜帶着Cookie一起。
有了Cookie我們就可以在很多地方看到一些似曾相識的廣告了!比如當你在某寶上買了什麼玩意,然後系統會推薦很多很多的相似的東西好讓你剁手!
Cookie是基於域名安全的,不同域名的Cookie是不能相互訪問的。這就牽扯到CSRF了,這個留在模板中來說。
1 from flask import Flask, request, make_response
2
3 app = Flask(__name__)
4
5
6 @app.route('/set_cookie')
7 def set_cookie():
8 # make_response()用來生成一些響應頭信息
9 # 參數作爲響應體
10 resp_header = make_response('用來生成響應頭信息')
11 # 設置cookie,key爲unmae, value爲python, 有效期爲1分鐘
12 resp_header.set_cookie('uname', 'python', max_age=60)
13 return resp_header
14
15
16 @app.route('/get_cookie')
17 def get_cookie():
18 name = request.cookies.get('uname',"沒有獲取到有效的cookie")
19 return name
20
21
22 if __name__ == "__main__":
23 print(app.url_map)
24 app.run(debug=True)
(3). session是什麼?
在做Cookie的實驗中,我們發現Cookie保存的信息是明文形式,這樣是很不安全的。而且又是保存在本地的,不是保存在服務器上面。這樣的話,我們一些很重要的私人信息是會很容易被盜取。那有沒有一種保存在服務器的祕鑰,而且又是被加密的,返回給瀏覽器的只是一個key呢?
那麼session就是這樣的,保存在服務器,而且是加密的,不容易被盜取。但是Session是依賴於Cookie的。
session在Flask中是一種請求上下文對象,用於處理HTTP請求中的一些數據。
如果要設置Session,那麼必須要設置SECRET_KEY變量。SECRET_KEY 的作用主要是提供一個值做各種 HASH,可在 Flask 和多個第三方擴展中使用。
在上面提到過,一些配置信息最好不要和業務邏輯混合在一起。要麼單獨存放在一個文件中,如settings.py,要麼存放在一個ini文件中,或者添加到系統環境變量裏。這麼做的好處是如果你不把這些文件暴露出去,那麼其他人就很難看到!在Flask中都有提供這些操作接口(app.config.from_xxx()),很方便
1 from flask import Flask, session
2
3 # 自定義的配置文件,爲了安全起見,裏面可以做一些數據庫連接、祕鑰設置...
4 from settings import MyConfig
5
6 app = Flask(__name__)
7
8 # 從.py的配置文件中加載一些配置變量,如DEBUG, SECRET_KEY,...
9 app.config.from_object(MyConfig)
10
11
12 @app.route('/session')
13 def set_session():
14 session['name'] = 'python'
15 return '設置session成功'
16
17
18 if __name__ == "__main__":
19 print(app.url_map)
20 app.run()
五、請求鉤子
在Flask中有一個請求鉤子,就是4個裝飾器。但是在其他框架中,如Django、Scrapy中被稱爲中間件,所以也可以把請求鉤子稱作是中間件!這其實是一種編程思想(AOP切面編程)的體現。對AOP編程感興趣的讀者可以自行搜索瞭解一下!這裏主要講講Flask中的請求鉤子。
Flask中的請求鉤子分別是:before_first_request、before_request、after_request、teardown_request。這些請求鉤子有什麼作用呢?可以把他們想象成C++/Java中的構造函數和析構函數,即做一些初始化和銷燬的工作!其中最主要的功能還是在請求結束時,指定數據的交互格式,如指定Json格式。
[email protected]_first_request:在處理第一個請求前被執行,而且只會執行一次。
[email protected]_request:每次請求前被執行,可以做一些預檢工作,如果不滿足某些條件就return,然後視圖就不會被執行了
[email protected]_request:在每個視圖執行完畢之後並且沒有錯誤時調用,可以設置一些響應頭信息,等等操作。必須返回response
[email protected]_request:每次請求後被調用,接受一個參數:錯誤信息。
1 from flask import Flask, request, abort
2
3 from settings import MyConfig
4
5 app = Flask(__name__)
6
7 # 配置信息
8 app.config.from_object(MyConfig)
9
10
11 # 第一次請求前被調用,相當於__init__方法
12 @app.before_first_request
13 def before_first_request():
14 print('我只會被調用一次哦!')
15
16
17 # 在每次請求前被調用,可以做一些請求檢驗
18 # 如果請求的檢驗不成功,可以直接在此方法中進行響應,直接return後,不在繼續往下執行
19 @app.before_request
20 def before_request():
21 print(request.args.get('wd', '沒有找到查詢參數'))
22 if "?" not in request.url:
23 # 這個return 語句返回個客戶端瀏覽器,並作爲響應體
24 return 'url中沒有查詢參數'
25 print('你的請求URL:%s' % request.url)
26 # abort(500)
27
28
29 # 執行完視圖函數之後被調用,並且會把視圖函數生成的響應傳入,可以對response做一些設置
30 #
31 @app.after_request
32 def after_request(response):
33 # print(response.headers)
34
35 # if response.headers.startswith('text'):
36 # response.headers['Content-Type'] = 'application/json'
37 print('對響應信息做了一些更改!')
38 # 必須返回一個response
39 return response
40
41
42 # 在每次請求之後都會被調用,會接受一個參數,參數是服務器出現的錯誤信息
43 @app.teardown_request
44 def teardown_request(e):
45 print('teardown_request')
46 print(e)
47
48
49 @app.route('/args/<int:p1>')
50 def index1(p1):
51 return "接收到參數%s" % p1
52
53
54 @app.route('/args/<int:p2>')
55 def index2(p2):
56 return "接收到參數%s" % p2
57
58
59 if __name__ == '__main__':
60 app.run()
六、上下文對象
1. 什麼是上下文?
上下文可以當成一個容器,裏面保存着從開始到目前爲止發生的一些事情,就相當於一個日誌一樣。在Flask中,有兩種的上下文:請求上下文(request context)和應用上下文(application context)。
2. 請求上下文:
請求對象指每次發生HTTP請求時,在Flask對象內部創建的對象。
請求上下文包括:request對象和session對象。
request:主要保存着當前本次請求的相關數據,上文提到的那些常用屬性。針對的是HTTP請求。
session:用來保存請求會話中的信息,主要是用戶信息。
3.應用上下文:
應用對象是當調用app=Flask(__name__)時創建的對象,主要是幫助request獲取當前的應用。
應用上下文包括:current_app和g對象
current_app:用於存儲程序中的變量,主要是做日誌使用,生命週期比request上下文長
g:是一個臨時變量,保存的是當期那請求的全局變量,不同的請求會有不同的全部變量。
4.區別:
請求上下文主要是保存客戶端與服務器交互的數據;應用上下文是在程序運行過程中,保存着一些配置信息。
一般應用上下文的生命週期長於請求上下的,但只是current_app。