Flask 框架 - 檢視及路由 - 2
1 學習目標
-
能夠寫出帶有引數的路由及檢視函式
-
能夠說出 url_for 函式的作用
-
能夠說出自定義轉換器的步驟
2 路由基本定義
- 明確路由定義的引數,請求方式指定
- PostMan 的使用
2.1 指定路由地址
# 指定訪問路徑為 demo1
@app.route('/demo1')
def demo1():
return 'demo1'
2.2 給路由傳參示例
有時我們需要將同一類 URL 對映到同一個檢視函式處理,比如:使用同一個檢視函式來顯示不同使用者的個人資訊。
# 路由傳遞引數 @app.route('/user/<user_id>') def user_info(user_id): return 'hello %s' % user_id
- 路由傳遞的引數預設當做 string 處理,也可以指定引數的型別 <轉換器名字:引數名>
# 路由傳遞引數
@app.route('/user/<int:user_id>')
def user_info(user_id):
return 'hello %d' % user_id
這裡指定int,尖括號中的內容是動態的,在此暫時可以理解為接受 int 型別的值,實際上 int 代表使用 IntegerConverter 去處理 url 傳入的引數
無論 user_id 是什麼型別,都可以用 %s 來輸出
2.3 指定請求方式
在 Flask 中,定義一個路由,預設的請求方式為:
- GET
- OPTIONS(自帶)
- HEAD(自帶)
如果想新增請求方試,那麼可以如下指定:
@app.route('/demo2', methods=['GET', 'POST'])
def demo2():
# 直接從請求中取到請求方式並返回
return request.method
demo2 請求方式為:
3 使用 PostMan 對請求進行測試
3.1 安裝 PostMan 外掛
PostMan 是一款功能強大的網頁除錯與傳送網頁 HTTP 請求的 Chrome 外掛,可以直接去對我們寫出來的路由和檢視函式進行除錯,作為後端程式設計師是必須要知道的一個工具。
安裝方式1:去 Chrome 商店直接搜尋 PostMan 擴充套件程式進行安裝
安裝方式2:https://www.getpostman.com/ 官網下載桌面版
安裝方式3:將已下載好的 PostMan 外掛資料夾拖入到瀏覽器
開啟 Chrome 的擴充套件程式頁面,開啟 開發者模式 選項
將外掛資料夾拖入到瀏覽器(或者點選載入已解壓的擴充套件程式選擇資料夾)
使用 PostMan,開啟之後,會彈出註冊頁面,選擇下方的 Skip this,go straight to the app
進行程式
3.2 postman使用
我們使用postman來測試一下這個函式:
使用如下:
4 檢視常用邏輯
- 返回 JSON
- 重定向
- url_for
- 自定義狀態碼
4.1 JSON資料格式&返回JSON
4.1.1 JSON 介紹
新建一個json檔案,我們來看一下json資料的個數。
json其實說白了就是一個字串,格式類似於python中的字典。
作用:用於瀏覽器與伺服器進行資料傳輸。
json:瀏覽器與伺服器直接進行資料傳輸的一種資料格式。
4.1.2 字典轉換為json
我們在程式碼中定義一個字典,然後將字典轉換為json格式的字串返回給瀏覽器
訪問結果:
驗證一下這個格式是否正確如下:(將上圖的json字串複製到下圖的大框中,然後點選校驗)
最終看到正確的json及說明是正確的json。
4.1.3 json轉換為字典
4.1.4 返回JSON
在使用 Flask 寫一個介面時候需要給客戶端返回 JSON 資料,在 Flask 中可以直接使用 jsonify 生成一個 JSON 的響應,並且以application/json的內容格式返回到瀏覽器.
# 返回JSON
@app.route('/demo4')
def demo4():
json_dict = {
"user_id": 10,
"user_name": "laowang"
}
# jsonify 會指定響應內容的資料格式(告訴客戶端,返回的資料格式是什麼)
return jsonify(json_dict)
直接返回json字串和通過jsonify返回json字串的區別:
jsonify:返回的時候content-type為application/json
直接返回:content-type為預設text/html
為什麼要用jsonify:這個跟前端邏輯有關係
不推薦使用 json.dumps 轉成 JSON 字串直接返回,因為返回的資料要符合 HTTP 協議規範,如果是 JSON 需要指定 content-type:application/json
這裡大家先記住,如果要返回json格式的字串,並且讓前端知道是一個json格式的字串,那就必須設定content-type:application/json。
5 重定向
重定向到 京東官網
# 重定向
@app.route('/demo5')
def demo5():
return redirect('http://www.jd.com')
重定向到自己寫的檢視函式
可以直接填寫自己 url 路徑(這種寫法有缺陷,如果user的路徑修改了,那麼我們的重定向的url也需要修改)
也可以使用 url_for 生成指定檢視函式所對應的 url(推薦使用,需要導包)
語法:url_for(“函式名”)
@app.route('/demo1')
def demo1():
return 'demo1'
# 重定向
@app.route('/demo5')
def demo5():
return redirect(url_for('demo1'))
重定向到帶有引數的檢視函式
在 url_for 函式中傳入引數
# 路由傳遞引數
@app.route('/user/<int:user_id>')
def user_info(user_id):
return 'hello %d' % user_id
# 重定向
@app.route('/demo5')
def demo5():
# 使用 url_for 生成指定檢視函式所對應的 url
return redirect(url_for('user_info', user_id=100))
6 自定義狀態碼
在 Flask 中,可以很方便的返回自定義狀態碼,以實現不符合 http 協議的狀態碼,例如:status code: 666
@app.route('/demo6')
def demo6():
return '狀態碼為 666', 666
還可以為自定義的狀態碼進行說明
語法: “狀態碼 狀態碼名字” (雙引號引起來 空格隔開)
7 正則匹配路由
7.1 預設轉換器的缺點:
如果我們現在想限制user/後邊只能有6個數字,int轉換器做不到
7.2 自定義轉換器
定義一個自定義的轉換器:RegexConverter
繼承BaseConverter基類,重寫regex屬性
訪問:
如果是4位數字,就找不到:
我們來看下原理:
看下int轉換器:IntegerConverter
注:基類中 regex 為類屬性,而重寫 regex 屬性實質上是添加了例項屬性 regex。
7.3 自定義轉換器升級
發現上面的正則轉換器,有點笨,只能處理一種正則表示式,所以需要進行升級。
7.3.1 實現步驟
在 web 開發中,可能會出現限制使用者訪問規則的場景,那麼這個時候就需要用到正則匹配,根據自己的規則去限定請求引數再進行訪問
具體實現步驟為:
- 匯入轉換器基類:在 Flask 中,所有的路由的匹配規則都是使用轉換器物件進行記錄
- 自定義轉換器:自定義類繼承轉換器基類
- 新增轉換器到預設的轉換器字典中
- 使用自定義轉換器實現自定義匹配規則
7.3.2 程式碼實現
匯入轉換器基類
from werkzeug.routing import BaseConverter
自定義轉換器:繼承轉換器基類
# 自定義正則轉換器
class RegexConverter(BaseConverter):
def __init__(self, url_map, *args):
super(RegexConverter, self).__init__(url_map)
# 將接受的第1個引數當作匹配規則進行儲存
self.regex = args[0]
新增轉換器到預設的轉換器字典中,並指定轉換器使用時名字為:re
app = Flask(__name__)
# 將自定義轉換器新增到轉換器字典中,並指定轉換器使用時名字為: re
app.url_map.converters['re'] = RegexConverter
使用轉換器去實現自定義匹配規則
當前此處定義的規則是:6位數字
# <re("\\d{6}"):user_id> 或者 <re(r"\d{6}"):user_id>
@app.route('/user/<re("[0-9]{6}"):user_id>')
def user_info(user_id):
return "user_id 為 %s" % user_id
執行測試:http://127.0.0.1:5000/user/123 ,如果訪問的url不符合規則,會提示找不到頁面
8 補充-startflask
配置:live Templates 活動模板, 配置完之後,就可以快速編碼-程式碼塊。
注意第七步:
原本不是change:而是define.
點選Define,彈出如下框. 選擇Python( 這一步的意思就是,你這個程式碼塊要在哪裡使用.我們在python程式碼中使用)
輸入startflask敲回車:
就會生成程式碼。
9 自定義轉換器其他兩個函式實現
9.1 轉換器 to_python
斷點除錯訪問:(如上圖,在36行打上斷點,然後訪問下邊的url)
觀察下圖可以發現,user_ids的值是一個字串:
但是我們想接收到的是一個列表怎麼辦?換句話說,如何才能在檢視函式中接收到的 user_ids 就是一個列表?
接下來就來看一下我們要分析的 to_python
先定義一個轉換器,並且重寫 to_python 如下
注:regex 還可以寫為:regex = r"(\d+,?)+\d$"
使用轉換器.
再次訪問之後,發現跟之前沒有區別。這裡的user_ids也沒有變成我們想要的列表.
怎麼辦呢? 做如下修改:
再次訪問:
斷點如下:
最終結果:
思考:這個int轉換器是如何將匹配到的字串轉換為int型別之後給user_id的?(提示:to_python)
to_python 分析:
9.2 轉換器to_url
9.2.1 問題
我們先來看一個例子:
新建demo3函式。重定向到demo2
上圖紅框程式碼說明:
其實demo2,在訪問的時候url應該是:127.0.0.1:5000/users/1,2,3,4 然後list轉換器將1,2,3,4轉換為列表。
那麼上圖中通過url_for傳遞資料user_ids資料的時候,應該傳遞字串:1,2,3,4
但是我們程式中可能就是一個user id的一個列表。
所以我們這裡就傳遞列表過去。
來訪問:
發現報錯。肯定報錯了。這是因為url中怎麼可能傳遞一個列表的資料呢?
上述url是經過瀏覽器編碼了。
其實應該是如下:
其實修改一下我們傳遞的資料即可,如下:
但是我們的邏輯中可能user的id本身是一個列表。傳的時候,我們確實也可以將列表轉換為一個字串,傳遞到下邊。但是這件事情,能不能別人幫咱們處理呢?
9.2.2 to_url介紹
來看一下to_url:
注意,demo3的程式碼,還應該是列表:
我們先來研究下to_url
訪問/demo3,斷點除錯如下:
發現to_url中的value是我們url_for傳遞的引數。user_ids。
得出如下結論:
那就簡單了。我們利用to_url,對value做一個轉換即可:
再次訪問:
斷點除錯如下:
斷點走完,最終結果:
9.3 總結
繼承於自定義轉換器之後,還可以實現 to_python 和 to_url 這兩個函式去對匹配引數做進一步處理:
to_python:
- 該函式引數中的 value 值代表匹配到的值,可輸出進行檢視
- 匹配完成之後,對匹配到的引數作最後一步處理再返回,比如:轉成 int 型別的值再返回:
class RegexConverter(BaseConverter):
def __init__(self, url_map, *args):
super(RegexConverter, self).__init__(url_map)
# 將接受的第1個引數當作匹配規則進行儲存
self.regex = args[0]
def to_python(self, value):
return int(value)
執行測試,在檢視函式中可以檢視引數的型別,由之前預設的 str 已變成 int 型別的值
to_url:
- 在使用 url_for 去獲取檢視函式所對應的 url 的時候,會呼叫此方法對 url_for 後面傳入的檢視函式引數做進一步處理
- 具體可參見 Flask 的 app.py 中寫的示例程式碼:ListConverter
9.4 系統自帶轉換器
DEFAULT_CONVERTERS = {
'default': UnicodeConverter,
'string': UnicodeConverter,
'any': AnyConverter,
'path': PathConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,
}
系統自帶的轉換器具體使用方式在每種轉換器的註釋程式碼中有寫,請留意每種轉換器初始化的引數。
10 異常捕獲
10.1 HTTP 異常主動丟擲
abort 方法
- 丟擲一個給定狀態程式碼的 HTTPException 或者 指定響應,例如想要用一個頁面未找到異常來終止請求,你可以呼叫 abort(404)。
- 引數:code – HTTP的錯誤狀態碼
# abort(404)
abort(500)
丟擲狀態碼的話,只能丟擲 HTTP 協議的錯誤狀態碼
常見的狀態碼:HTTP Status Code
10.2 捕獲錯誤
errorhandler 裝飾器
- 註冊一個錯誤處理程式,當程式丟擲指定錯誤狀態碼的時候,就會呼叫該裝飾器所裝飾的方法
引數:
- code_or_exception – HTTP的錯誤狀態碼或指定異常
例如統一處理狀態碼為500的錯誤給使用者友好的提示:
@app.errorhandler(500)
def internal_server_error(error):
return '伺服器搬家了'
捕獲指定異常
@app.errorhandler(ZeroDivisionError)
def zero_division_error(e):
return '除數不能為0'
11 請求鉤子
在客戶端和伺服器互動的過程中,有些準備工作或掃尾工作需要處理,比如:
- 在請求開始時,建立資料庫連線;
- 在請求開始時,根據需求進行許可權校驗;
- 在請求結束時,指定資料的互動格式;
為了讓每個檢視函式避免編寫重複功能的程式碼,Flask提供了通用設施的功能,即請求鉤子。
請求鉤子是通過裝飾器的形式實現,Flask支援如下四種請求鉤子:
- before_first_request
- 在處理第一個請求前執行
- before_request
- 在每次請求前執行
- 如果在某修飾的函式中返回了一個響應,檢視函式將不再被呼叫
- after_request
- 如果沒有丟擲錯誤,在每次請求後執行
- 接受一個引數:檢視函式作出的響應
- 在此函式中可以對響應值在返回之前做最後一步修改處理
- 需要將引數中的響應在此引數中進行返回
- teardown_request:
- 在每次請求後執行
- 接受一個引數:錯誤資訊,如果有相關錯誤丟擲
總結:請求鉤子類似於裝飾器,可以在不修改函式內部的邏輯前提下增加邏輯.
程式碼測試
from flask import Flask
from flask import abort
app = Flask(__name__)
# 在第一次請求之前呼叫,可以在此方法內部做一些初始化操作
@app.before_first_request
def before_first_request():
print("before_first_request")
# 在每一次請求之前呼叫,這時候已經有請求了,可能在這個方法裡面做請求的校驗
# 如果請求的校驗不成功,可以直接在此方法中進行響應,直接return之後那麼就不會執行檢視函式
@app.before_request
def before_request():
print("before_request")
# if 請求不符合條件:
# return "請求失敗!"
# 在執行完檢視函式之後會呼叫,並且會把檢視函式所生成的響應傳入,可以在此方法中對響應做最後一步統一的處理
@app.after_request
def after_request(response):
print("after_request")
response.headers["Content-Type"] = "application/json"
return response
# 請每一次請求之後都會呼叫,會接受一個引數,引數是伺服器出現的錯誤資訊
# 沒有報異常也會在index之後執行此函式,只不過沒有將異常資訊傳遞給error而已.
@app.teardown_request
def teardown_request(error):
print("teardown_request")
@app.route('/')
def index():
return 'index'
if __name__ == '__main__':
app.run(debug=True)
在第1次請求時的列印:
before_first_request
before_request
after_request
teardown_request
在第2次請求時的列印:
before_request
after_request
teardown_request