Flask修煉——模板!
內容概述:
to_python、to_url,
異常捕獲,
請求鉤子,
獲取請求引數,
狀態保持,
上下文,
Flask-Script 擴充套件,
模板
to_python、to_url
自定義轉換器
to_python: 可以對匹配到的引數進行處理並返回,在呼叫檢視函式前執行
to_url :在使用 url_for 的時候,對檢視函式傳的引數進行處理,處理完畢之後以便能夠進行路由匹配
異常捕獲
HTTP異常主動丟擲
abort 方法:丟擲一個給定狀態程式碼的 HTTPException 或者 指定響應,例如想要用一個頁面未找到異常來終止請求,你可以呼叫 abort(404)。
引數:code——HTTP 的錯誤狀態碼
捕獲錯誤
errorhandler 裝飾器 :註冊一個錯誤處理程式,當程式丟擲指定錯誤狀態碼的時候,就會呼叫該裝飾器所裝飾的方法
引數: code_or_exception – HTTP的錯誤狀態碼或指定異常
from flask import Flask
from flask import abort
app = Flask(__name__)
@app.route('/')
def index():
return 'index'
@app.route('/demo1')
def demo1 ():
# 主動丟擲 HTTP 指定錯誤程式碼
# abort(404)
a = 0
b = 1 / a
return 'demo1'
@app.errorhandler(404)
def page_not_found(error):
return "頁面不見了~"
@app.errorhandler(ZeroDivisionError)
def zero_division_error(error):
return "除數不能為0 "
if __name__ == '__main__':
app.run(debug=True)
請求鉤子
在客戶端和伺服器互動的過程中,有些準備工作或掃尾工作需要處理 ,為了讓每個檢視函式避免編寫重複功能的程式碼,Flask提供了通用設施的功能,即請求鉤子 .
請求鉤子是通過裝飾器的形式實現,Flask支援如下四種請求鉤子:
before_first_request: 在第一次請求之前會執行
before_request : 在每次請求之前會執行,可以在這裡對一些非法的請求進行阻止,那麼檢視函式將不再被呼叫
after_request: 在請求之後會執行,並且函式裡面接收一個引數:響應,還需要將響應進行返回
teardown_request: 在請求之後會執行,如果請求的函式有異常,會把具體異常丟擲
from flask import Flask
app = Flask(__name__)
@app.before_first_request
def before_first_request():
"""在第一次請求之前會訪問該函式"""
print('before_first_request')
@app.before_request
def before_request():
"""在每次請求之前會訪問該函式"""
print('before_request')
# 可以對一些非法的請求進行阻止
# if 如果ip在黑名單:
# return "在黑名單中"
@app.after_request
def after_request(response):
"""在請求之後會呼叫,並且函式裡面接收一個引數:響應,還需要將響應進行返回"""
print('after_request')
return response
@app.teardown_request
def teardown_request(error):
"""在請求之後會執行,如果請求的函式報有異常,會把具體異常傳入到此函式"""
print('teardown_request')
@app.route('/')
def index():
return 'index'
if __name__ == '__main__':
app.run(debug=True)
裝飾器路由的實現
Flask有兩大核心:Werkzeug和Jinja2
- Werkzeug實現路由、除錯和Web伺服器閘道器介面 - Jinja2實現了模板。
獲取請求引數
request:
request 就是flask中代表當前請求的 request 物件,其中一個請求上下文變數(理解成全域性變數,在檢視函式中直接使用可以取到當前本次請求)
常用的屬性如下:
屬性 | 說明 | 型別 |
---|---|---|
data | 記錄請求的資料,並轉換為字串 | * |
form | 記錄請求中的表單資料 | MultiDict |
args | 記錄請求中的查詢引數 | MultiDict |
cookies | 記錄請求中的cookie資訊 | Dict |
headers | 記錄請求中的報文頭 | EnvironHeaders |
method | 記錄請求使用的HTTP方法 | GET/POST |
url | 記錄請求的URL地址 | string |
files | 記錄請求上傳的檔案 | * |
from flask import Flask
from flask import request
app = Flask(__name__)
@app.route('/')
def index():
return 'index'
# OSError: [Errno 98] Address already in use
# 埠被佔用,是程式沒有完全關閉,後臺還在執行
# 在 ubuntu 中使用命令: netstat -apn | grep 5000 找到 5000 埠對應的 pid
# 使用 kill -9 pid 命令進行清除
@app.route('/upload', methods=['POST'])
def upload():
file = request.files.get('pic')
file.save('aaa.png')
return 'success'
@app.route('/data', methods=['POST'])
def data():
data = request.data
print(data)
return 'ok'
if __name__ == '__main__':
app.run(debug=True)
狀態保持
- http 是一種無狀態協議,瀏覽器請求伺服器是無狀態的。
- 無狀態:指一次使用者請求時,瀏覽器、伺服器無法知道之前這個使用者做過什麼,每次請求都是一次新的請求。
- 無狀態原因:瀏覽器與伺服器是使用 socket 套接字進行通訊的,伺服器將請求結果返回給瀏覽器之後,會關閉當前的 socket 連線,而且伺服器也會在處理頁面完畢之後銷燬頁面物件。
- 實現狀態保持主要有兩種方式:
- 在客戶端儲存資訊使用
Cookie
- 在伺服器端儲存資訊使用
Session
無狀態協議:
- 協議對於事務處理沒有記憶能力
- 對同一個 url 請求沒有上下文關係
- 每次的請求都是獨立的,它的執行情況和結果與前面的請求和之後的請求是無直接關係的,它不會受前面的請求應答情況直接影響,也不會直接影響後面的請求應答情況
- 伺服器中沒有儲存客戶端的狀態,客戶端必須每次帶上自己的狀態去請求伺服器
- 人生若只如初見
cookie
- Cookie是儲存在瀏覽器中的一段純文字資訊,建議不要儲存敏感資訊如密碼,因為電腦上的瀏覽器可能被其它人使用
- Cookie基於域名安全,不同域名的Cookie是不能互相訪問的
- 瀏覽器的同源策略,所謂同源是指,域名,協議,埠相同。不同源的客戶端腳(javascript、ActionScript)本在沒明確授權的情況下,不能讀寫對方的資源。
make_response 獲取 response 物件
set_cookie(‘username’, ‘xiaohei’, max_age=3600) 設定cookie,設定過期時間
request.cookies.get() 獲取cookie
cookie 流程:
設定 cookie: 通過響應帶給瀏覽器
獲取 cookie:從請求中獲取
- 瀏覽器傳送登入請求,傳送請求報文,帶上要登入的賬號和密碼
- 伺服器校驗賬號和密碼
- 如果正確就給出響應,帶上設定好的 cookie ,
response.set_cookie('username', 'xiaohei')
- 瀏覽器會自動將 cookie 儲存起來
- 下次請求該網站時,會把 cookie 請求帶到伺服器,伺服器取到傳過來的 cookie,根據 cookie 就可以知道是否登入,是誰登入
from flask import Flask
from flask import make_response
from flask import request
app = Flask(__name__)
@app.route('/')
def index():
user_id = request.cookies.get('user_id')
user_name = request.cookies.get('user_name')
return "%s-----%s" % (user_id, user_name)
@app.route('/login')
def login():
# 預設判斷賬號與密碼是正確的
response = make_response('success')
# 設定 cookie
response.set_cookie('user_id', '1', max_age=3600)
response.set_cookie('user_name', 'xiaohei', max_age=3600)
return response
@app.route('/logout')
def logout():
response = make_response('success')
response.delete_cookie('user_id')
response.delete_cookie('user_name')
return response
if __name__ == '__main__':
app.run(debug=True)
session
- 對於敏感、重要的資訊,建議要儲存在伺服器端,不能儲存在瀏覽器中,如使用者名稱、餘額、等級、驗證碼等資訊
- 在伺服器端進行狀態保持的方案就是
Session
- Session依賴於Cookie
session 流程
- 傳送登入請求,傳送請求報文,帶上要登入的賬號和密碼
- 伺服器處理請求:校驗密碼,儲存使用者資訊到伺服器,每個使用者資訊對應一個 sid
- 伺服器給出相應,把 sid 返回給瀏覽器(cookie)
- 瀏覽器把 sid 儲存起來
- 下次請求時預設會帶上 sid, 伺服器可以通過請求取到 sid,在通過 sid 取到對應的使用者資訊,取到資訊就可以知道是否登入,是誰登入
from flask import Flask
from flask import session
app = Flask(__name__)
# 使用 session 的話,需要配置 secret_key
app.config['SECRET_KEY'] = 'asdfghj'
@app.route('/')
def index():
user_id = session.get('user_id', '')
user_name = session.get('user_name', '')
return '%s -- %s' % (user_id, user_name)
@app.route('/login')
def login():
# 假裝校驗成功
session['user_id'] = "1"
session['user_name'] = 'xiaohei'
return 'success'
@app.route('/logout')
def logout():
session.pop('user_id')
session.pop('user_name')
return 'success'
if __name__ == '__main__':
app.run(debug=True)
上下文
上下文:相當於一個容器,儲存了 Flask 程式執行過程中的一些資訊。
pycharm 中移動整行程式碼的快捷鍵: shift + alt + 上下鍵
請求上下文
# 請求上下文中的變數 from flask import session from flask import request
request: 封裝了 HTTP 請求的內容,針對的是 http 請求
session:用來記錄請求會話中的資訊,針對的是使用者資訊。
應用上下文
它的字面意思是 應用上下文,但它不是一直存在的,它只是request context 中的一個對 app 的代理(人),所謂local proxy。它的作用主要是幫助 request 獲取當前的應用,它是伴 request 而生,隨 request 而滅的。
# 應用上下文中的變數 from flask import current_app from flask import g
current_app: 應用程式上下文,用於儲存應用程式中的變數。
g 變數:作為 flask 程式全域性的一個臨時變數,充當者中間媒介的作用,我們可以通過它傳遞一些資料,g 儲存的是當前請求的全域性變數,不同的請求會有不同的全域性變數,通過不同的thread id區別
注意:不同的請求,會有不同的全域性變數
from flask import Flask
# 應用上下文中的變數
from flask import current_app
from flask import g
# 請求上下文中的變數
from flask import session
from flask import request
app = Flask(__name__)
# print(request.method)
# print(session.get['user_id', ''])
# print(current_app.config.get('DEBUG'))
@app.route('/')
def index():
# print(request.method)
print(current_app.config.get('DEBUG'))
return 'index'
if __name__ == '__main__':
app.run(debug=True)
Flask-Script 擴充套件
通過使用Flask-Script擴充套件,我們可以在Flask伺服器啟動的時候,通過命令列的方式傳入引數。而不僅僅通過app.run()方法中傳參,
pip install flask-script
from flask import Flask
from flask_script import Manager
app = Flask(__name__)
# 建立 manager 與 app 進行關聯
manager = Manager(app)
# 可以通過命令列在執行的時候指定執行的埠
@app.route('/')
def index():
return 'index'
if __name__ == '__main__':
# 需要使用 manager 去執行
manager.run()
模板
在前面的示例中,檢視函式的主要作用是生成請求的響應,這是最簡單的請求。實際上,檢視函式有兩個作用:處理業務邏輯和返回響應內容。在大型應用中,把業務邏輯和表現內容放在一起,會增加程式碼的複雜度和維護成本。本節學到的模板,它的作用即是承擔檢視函式的另一個作用,即返回響應內容。
- 模板其實是一個包含響應文字的檔案,其中用佔位符(變數)表示動態部分,告訴模板引擎其具體的值需要從使用的資料中獲取
- 使用真實值替換變數,再返回最終得到的字串,這個過程稱為“渲染”
- Flask是使用 Jinja2 這個模板引擎來渲染模板
使用模板的好處:
- 檢視函式只負責業務邏輯和資料處理(業務邏輯方面)
- 而模板則取到檢視函式的資料結果進行展示(檢視展示方面)
- 程式碼結構清晰,耦合度低
jinja2
- Jinja2:是 Python 下一個被廣泛應用的模板引擎,是由Python實現的模板語言,他的設計思想來源於 Django 的模板引擎,並擴充套件了其語法和一系列強大的功能,其是Flask內建的模板語言。
- 模板語言:是一種被設計來自動生成文件的簡單文字格式,在模板語言中,一般都會把一些變數傳給模板,替換模板的特定位置上預先定義好的佔位變數名。
- Flask提供的 render_template 函式封裝了該模板引擎
- render_template 函式的第一個引數是模板的檔名,後面的引數都是鍵值對,表示模板中變數對應的真實值。
使用
- {{}} 來表示變數名,這種 {{}} 語法叫做變數程式碼塊
- 用 {%%} 定義的控制程式碼塊,可以實現一些語言層次的功能,比如迴圈或者if語句
- 使用 {# #} 進行註釋,註釋的內容不會在html中被渲染出來
from flask import Flask
from flask import render_template
app = Flask(__name__)
@app.route('/')
def index():
return 'index'
@app.route('/demo1')
def demo1():
my_int = 10
my_str = "<h1>哈哈哈</h1>"
my_list = [1, 24, 5, 62, 6]
my_dict = {
"id": "1",
"name": "xiaohei"
}
return render_template('demo6_template.html',
my_int=my_int,
my_str=my_str,
my_list=my_list,
my_dict=my_dict,
)
if __name__ == '__main__':
app.run(debug=True)