[toc]
## 一:Flask請求響應
```python
# 導入模塊
from flask import Flask,jsonify
from flask import views
from flask import Flask
from flask import request
from flask import render_template
from flask import redirect
from flask import make_response
app = Flask(__name__)
app.debug=True
```
```python
@app.route('/login.html', methods=['GET', "POST"])
def login():
```
### 1.請求相關信息
```python
request.method # 提交的方法
print(request.args.get('name')) # get請求提交的數據---GET
print(request.form) # post請求提交數據----POST
print(request.values) # get和post的彙總
print(request.query_string) # b'name=lqz&age=19'
request.args # get請求提及的數據
request.form # post請求提交的數據
request.values # post和get提交的數據總和
request.cookies # 客戶端所帶的cookie
request.headers # 請求頭
request.path # 不帶域名,請求路徑
request.full_path # 不帶域名,帶參數的請求路徑
request.url # 帶域名帶參數的請求路徑
request.base_url # 帶域名請求路徑
request.url_root # 域名
request.host_url # 域名
request.host # 127.0.0.1:500
request.files # 獲取文件對象
obj = request.files['the_file_name'] # .files獲取文件對象
obj.save('/var/www/uploads/' + secure_filename(f.filename))
```
### 2.flask新手四件套
```python
return "字符串"
return render_template('html模板路徑',**{})
return redirect('/index.html')
return jsonify({'k1':'v1'}) # 返回json格式
```
### 3.響應相關信息(響應response增加數據返回)
```python
# 將render_template('index.html')生成對象,寫到make_response對象內
response = make_response(render_template('index.html'))
response = make_response('hello')
# response是flask.wrappers.Response類型
response.delete_cookie('session')
response.set_cookie('name', 'lqz')
response.headers['X-Something'] = 'A value'
return response
# return "內容"
if __name__ == '__main__':
app.run(port=8888)
```
## 二:session
### 1.session與cookie簡介
```python
cookie:存放在客戶端的鍵值對
session:存放在客戶端的鍵值對
token:存放在客戶端,通過算法來校驗
```
### 2.在使用session之前必須現在設置一下密鑰
```python
app.secret_key="asdas" #值隨便
```
### 3.dajngo中session與Flask的session差別
```python
# 1.在django中發什麼三件事:
1,生成一個隨機的字符串 2 往數據庫存 3 寫入cookie返回瀏覽器
# 2.在flask中沒有數據庫,但session是怎樣實現的?
1.生成一個密鑰寫入這個cookie,然後下次請求的時候,通過這個cookie解密,然後賦值給session
```
### 4.session使用
```python
-增:session['name']=lqz
-查:session.get('name')
-刪:session.pop('name')
```
### 5.set_cookie其他參數
```python
key, 鍵
value='', 值
max_age=None, 超時時間 cookie需要延續的時間(以秒爲單位)如果參數是\ None`` ,這個cookie會延續到瀏覽器關閉爲止
expires=None, 超時時間(IE requires expires, so set it if hasn't been already.)
path='/', Cookie生效的路徑,/ 表示根路徑,特殊的:根路徑的cookie可以被任何url的頁面訪問,瀏覽器只會把cookie回傳給帶有該路徑的頁面,這樣可以避免將cookie傳給站點中的其他的應用。
domain=None, Cookie生效的域名 你可用這個參數來構造一個跨站cookie。如, domain=".example.com"所構造的cookie對下面這些站點都是可讀的:www.example.com 、 www2.example.com 和an.other.sub.domain.example.com 。如果該參數設置爲 None ,cookie只能由設置它的站點讀取
secure=False, 瀏覽器將通過HTTPS來回傳cookie
httponly=False 只能http協議傳輸,無法被JavaScript獲取(不是絕對,底層抓包可以獲取到也可以被覆蓋)
```
### 6.整體代碼
```python
from flask import Flask,jsonify
from flask import views
from flask import Flask,session
from flask import request
from flask import render_template
from flask import redirect
from flask import make_response
app = Flask(__name__)
app.debug=True
app.secret_key='sdfsdfsadfasdf'
app.session_interface
@app.route('/login.html', methods=['GET', "POST"])
def login():
session['name']='lqz'
response=make_response('hello')
# response.set_cookie('name', 'lqz')
return response
@app.route('/index', methods=['GET', "POST"])
def index():
print(session.get('name'))
return '我是首頁'
if __name__ == '__main__':
app.run(port=8080)
```
## 三:源碼分析SecureCookieSessionInterface
### 1.f'lask框架session源碼分析
![image-20220508105027958](https://s2.loli.net/2022/05/08/QHs7tWZOoKcIJFX.png)
### 2.分析SecureCookieSessionInterface
![image-20220508105227600](https://s2.loli.net/2022/05/08/u68pb2cseO1gj9h.png)
### 3.分析save_seesion響應與open_session請求來的時候
```python
1.save_seesion
-響應的時候,把session中的值加密序列化放大到了cookie中,返回到瀏覽器中
# save_session
"""
# 將session設置成字典類型序列化轉換成json字符串
val = self.get_signing_serializer(app).dumps(dict(session))
# 1.響應對象內設置session
response.set_cookie(
# 2.session的id
app.session_cookie_name,
# 3.session的value
val,
expires=expires,
)
"""
2.open_session
-請求來了,從cookie中取出值,反解,生成session對象,以後再視圖函數中直接用sessoin就可以了。
# open_session
"""
# 1.取出request.cookies中的value值
val = request.cookies.get(app.session_cookie_name)
# 2.將session的value值取出來反序列化
data = s.loads(val, max_age=max_age)
# 3.返回session(data)
return self.session_class(data)
"""
```
### 4.整體代碼
```python
from flask import Flask,jsonify
from flask import views
from flask import Flask,session
from flask import request
from flask import render_template
from flask import redirect
from flask import make_response
app = Flask(__name__)
app.debug=True
app.secret_key='sdfsdfsadfasdf'
app.session_interface
@app.route('/login.html', methods=['GET', "POST"])
def login():
session['name']='lqz'
response=make_response('hello')
# response.set_cookie('name', 'lqz')
return response
@app.route('/index', methods=['GET', "POST"])
def index():
print(session.get('name'))
return '我是首頁'
if __name__ == '__main__':
app.run(port=8080)
```
## 四:閃現
```python
-設置:flash('aaa')
-取值:get_flashed_message()
-假設在a頁面操作出錯,跳轉到b頁面,在b頁面顯示a頁面的錯誤信息
```
#### 1.示例:
```python
from flask import Flask,flash,get_flashed_messages,request,redirect
app = Flask(__name__)
app.secret_key = 'asdfasdf'
@app.route('/user', methods=['GET', "POST"])
def login():
try:
a=[1,2,3]
print(a[9])
except Exception as e:
print(e)
# 閃現普通使用(放在某個位置)
flash(str(e))
# 高級使用(閃現分類)
flash('超時錯誤', category="x1")
flash('xx錯誤', category="x3")
return response
@app.route('/error', methods=['GET', "POST"])
def error():
# 1.取出閃現(錯誤信息)
errors=get_flashed_messages()
# 2.取出閃現(高級使用分類)
errors=get_flashed_messages(category_filter=['x1'])
return render_template('error.html',errors=errors)
if __name__ == '__main__':
app.run()
```
![閃現](https://s2.loli.net/2022/05/08/yL4iVzH2AqUP9CJ.gif)
## 五:請求擴展
### 1 before_request
```python
# 請求來了會先走before_request
1.類比django中間件中的process_request,寫多個執行順序是從上往下
```
```python
#基於它做用戶登錄認證
@app.before_request
def process_request(*args,**kwargs):
if request.path == '/login':
return None
user = session.get('user_info')
if user:
return None
return redirect('/login')
```
### 2 after_request
```python
# 從下往上,執行完了,響應走的時候執行
1.類比django中間件中的process_response,每一個請求之後綁定一個函數,如果請求沒有異常
```
```python
@app.after_request
def process_response1(response):
print('process_response1 走了')
return response
```
### 3 before_first_request
```python
# 只會執行一次,程序啓動以後,第一個訪問的會觸發,以後再也不會了
1.第一次請求時,跟瀏覽器無關
```
```python
@app.before_first_request
def first():
pass
```
### 4 teardown_request
```python
# 不管當次請求是否出異常,都會執行,出了異常,e就是異常對象,debug=False模式下,必須在上線模式下,False
1.每一個請求之後綁定一個函數,即使遇到了異常
# 作用:日誌記錄,
```
```python
@app.teardown_request
def ter(e):
pass
```
### 5 errorhandler
```python
# 只要是404錯誤,都會走它
1.路徑不存在時404,服務器內部錯誤500
```
```python
@app.errorhandler(404)
def error_404(arg):
return "404錯誤了"
```
### 6 template_global
##### 自定義標籤
```python
@app.template_global()
def sb(a1, a2):
return a1 + a2
#{{sb(1,2)}}
```
### 7 template_filter
##### 自定義過濾器
```python
@app.template_filter()
def db(a1, a2, a3):
return a1 + a2 + a3
#{{ 1|db(2,3)}}
```
### 8.總結:
```python
1 重點掌握before_request和after_request,
2 注意有多個的情況,執行順序
3 before_request請求攔截後(也就是有return值),response所有都執行
```
## 六:中間件(瞭解)
```python
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World!'
# 模擬中間件
class Md(object):
def __init__(self,old_wsgi_app):
self.old_wsgi_app = old_wsgi_app
def __call__(self, environ, start_response):
print('開始之前')
ret = self.old_wsgi_app(environ, start_response)
print('結束之後')
return ret
if __name__ == '__main__':
#1我們發現當執行app.run方法的時候,最終執行run_simple,最後執行app(),也就是在執行app.__call__方法
#2 在__call__裏面,執行的是self.wsgi_app().那我們希望在執行他本身的wsgi之前做點事情。
#3 所以我們先用Md類中__init__,保存之前的wsgi,然後我們用將app.wsgi轉化成Md的對象。
#4 那執行新的的app.wsgi_app,就是執行Md的__call__方法。
#把原來的wsgi_app替換爲自定義的,
app.wsgi_app = Md(app.wsgi_app)
app.run()
```
請求所有的流程
```python
ctx = self.request_context(environ)
error = None
try:
try:
ctx.push()
#根據路徑去執行視圖函數,視圖類
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
except: # noqa: B001
error = sys.exc_info()[1]
raise
return response(environ, start_response)
finally:
#不管出不出異常,都會走這裏
if self.should_ignore_error(error):
error = None
ctx.auto_pop(error)
```
## 藍圖
對程序進行目錄結構劃分
### 1.使用步驟
```python
1.實例化得到一個藍圖對象(可以指定直接的靜態文件和模板路徑)
2.在app中註冊藍圖(可以指定前綴)
3.以後再寫路由裝飾器,使用藍圖對象的.route
```
### 2.不使用藍圖,自己分文件
目錄結構:
```python
-templates
-views
-__init__.py
-user.py
-order.py
-app.py
```
app.py
```python
from views import app
if __name__ == '__main__':
app.run()
```
init.py
```python
from flask import Flask,request
app = Flask(__name__)
#不導入這個不行
from . import account
from . import order
from . import user
```
user.py
```python
from . import app
@app.route('/user')
def user():
return 'user'
```
order.py
```python
from . import app
@app.route('/order')
def order():
return 'order'
```
### 3.使用藍圖之中小型系統
詳見代碼:pro_flask_簡單應用程序目錄示例.zip
目錄結構:
```python
-flask_pro
-flask_test
-__init__.py
-static
-templates
-views
-order.py
-user.py
-manage.py
```
_*init*.py
```python
from flask import Flask
app=Flask(__name__)
from flask_test.views import user
from flask_test.views import order
app.register_blueprint(user.us)
app.register_blueprint(order.ord)
```
manage.py
```python
from flask_test import app
if __name__ == '__main__':
app.run(port=8008)
```
user.py
```python
from flask import Blueprint
us=Blueprint('user',__name__)
@us.route('/login')
def login():
return 'login'
```
order.py
```python
from flask import Blueprint
ord=Blueprint('order',__name__)
@ord.route('/test')
def test():
return 'order test'
```
### 4.使用藍圖之大型系統
詳見代碼:pro_flask_大型應用目錄示例.zip
總結:
```python
1 xxx = Blueprint('account', **name**,url_prefix='/xxx') :藍圖URL前綴,表示url的前綴,在該藍圖下所有url都加前綴
2 xxx = Blueprint('account', name,url_prefix='/xxx',template_folder='tpls'):給當前藍圖單獨使用templates,向上查找,當前找不到,會找總templates
3 藍圖的befort_request,對當前藍圖有效
4 大型項目,可以模擬出類似於django中app的概念
```
## 七:flask 項目演示
```python
# 1 創建一個庫movie
# 2 手動把表同步進去
-modes.py,解開註釋,右鍵執行
# 3 安裝項目依賴
-flask-sqlalchemy
-flask_script
-flask_redis
-flask_wtf
# 4 命令行中運行
python3 manage.py runserver
# 5 後臺管理中rbac控制
```