一、flask的生命周期

客戶端--->wsgi應用程序->全局鉤子--> 路由 --> 視圖 --> 路由---> 全局鉤子 ---> wsgi應用程序---> 客戶端

二、請求

文檔: https://flask.palletsprojects.com/en/2.0.x/api/#flask.Request

  • request:flask中代表當前請求的 request 對象

  • 作用:在視圖函數中取出本次客戶端的請求數據

  • 導入from flask import request

  • 代碼位置

  • 代理類 from flask.app import Request ---> from flask.globals.Request

  • 源碼類:from flask.wrappers.Request

  • 基類:from werkzeug.wrappers import Request as RequestBase

request,常用的屬性如下

屬性說明類型
data 記錄請求體的數據,并轉換為字符串 只要是通過其他屬性無法識別轉換的請求體數據 最終都是保留到data屬性中 例如:有些公司開發微信小程序,原生IOS或者安卓,這一類客戶端有時候發送過來的數據就不一樣是普通的表單,查詢字符串或ajax bytes類型
form 記錄請求中的html表單數據 ImmutableMultiDict
args 記錄請求中的查詢字符串,也可以是query_string ImmutableMultiDict
cookies 記錄請求中的cookie信息 Dict
headers 記錄請求中的請求頭 ImmutableMultiDict
method 記錄請求使用的HTTP方法 GET/POST
url 記錄請求的URL地址 string
files 記錄請求上傳的文件列表 ImmutableMultiDict
json 記錄ajax請求的json數據 Dict

 

2.1、獲取請求中的各項數據

1、獲取查詢字符串,代碼:

 1 from flask import Flask, request
 2 from werkzeug.datastructures import ImmutableMultiDict
 3 from werkzeug.wrappers import Request as RequestBase
 4 from flask.wrappers import Request
 5 # 項目實例應用對象
 6 app = Flask(__name__)
 7 
 8 # 加載配置
 9 app.config.update({
10     "DEBUG": True
11 })
12 
13 
14 # 在http的常用請求方法中,delete和get是沒有請求體的!!!
15 
16 
17 @app.route(rule="/data", methods=["post", "put", "patch"])
18 def data():
19     """獲取請求體"""
20     # 獲取原生的請求體數據[當request對象的其他屬性沒法接受請求體數據時,會把數據保留在data中,如果有request對象的屬性處理了請求體數據,則data就不再保留]
21     # print(request.data)  # 如果客戶端上傳的是xml文檔,html格式,二進制流格式,base64格式,就要使用data來接收
22 
23     """
24     1. 沒有上傳任何數據:
25         b''
26     2. 上傳json數據
27         b'{\n    "username": "xiaoming",\n    "age": 16\n}'
28     3. 上傳表單數據
29         b''
30     4. 上傳xml數據
31         b'<goods-list>\n    <goods price="100">\xe5\x95\x86\xe5\x93\x81\xe6\xa0\x87\xe9\xa2\x981</goods>\n    <goods price="200">\xe5\x95\x86\xe5\x93\x81\xe6\xa0\x87\xe9\xa2\x982</goods>\n</goods-list>'
32     """
33 
34     # 接收表單上傳的數據
35     # print(request.form)
36     """
37     ImmutableMultiDict([('name', '小明'), ('age', '19')])
38     """
39 
40     # 接收ajax上傳的json數據
41     # print(request.json)     # {"username": "xiaoming", "age": 16}
42     # print(request.is_json)  # True 往往用于判斷是否是ajax請求
43 
44     # 上傳文件列表 HTML必須以<form method="post" enctype="multipart/form-data"> # 表單屬性才能上傳文件
45     # print(request.files)
46     """
47     ImmutableMultiDict([('avatar', <FileStorage: 'avatar.jpg' ('image/jpeg')>)])
48     """
49 
50     # 接受上傳文件
51     # avatar = request.files["avatar"]
52     # print(avatar)
53     """
54     <FileStorage: 'avatar.jpg' ('image/jpeg')>
55     from werkzeug.datastructures import FileStorage
56     FileStorage,上傳文件處理對象,flask封裝的一個上傳文件處理對象,可以允許我們直接調用對應的方法進行文件的存儲處理, 
57     也可以結合其他的ORM模塊像djangoORM那樣通過模型操作對上傳自動存儲處理
58     """
59     # 處理上傳文件[一般不會這么做!!!而是采用專業的第三方存儲設備來存儲,]
60     # from pathlib import Path
61     # save_path = str(Path(__file__).parent / "uploads/avatar.jpeg")
62     # avatar.save(save_path)
63 
64     # 獲取請求頭信息
65     print(request.headers)  # 獲取全部的而請求頭信息
66     print(request.headers.get("Host"))  # 127.0.0.1:5000,客戶端請求地址,也相當于服務器地址
67 
68     # 獲取客戶端發送過來的自定義請求頭
69     print(request.headers.get("company"))  # beijing,不存在的鍵的結果:None,存在則得到就是值,
70     print(request.headers.get("token"))    # jwt...xxx
71 
72     # 獲取客戶端的請求頭中的相關數據
73     print(request.user_agent)   # 用戶訪問服務器時使用的網絡代理,一般就是瀏覽器標記信息,PostmanRuntime/7.26.10
74     print(request.remote_addr)  # 客戶端遠程地址
75     print(request.server)       # 服務端的端點,格式:(IP, 端口)
76 
77     # 獲取請求方法
78     print(request.method)  # POST
79 
80     # 本次請求的url地址
81     print(request.url)  # http://127.0.0.1:5000/data
82     print(request.root_url)  # 根路徑
83     print(request.path)      # /data
84 
85 
86     return "獲取請求體"
87 
88 
89 if __name__ == '__main__':
90     app.run()

2、獲取請求體,代碼:

from flask import Flask, request
from werkzeug.datastructures import ImmutableMultiDict
from werkzeug.wrappers import Request as RequestBase
from flask.wrappers import Request
# 項目實例應用對象
app = Flask(__name__)

# 加載配置
app.config.update({
    "DEBUG": True
})


# 在http的常用請求方法中,delete和get是沒有請求體的!!!


@app.route(rule="/data", methods=["post", "put", "patch"])
def data():
    """獲取請求體"""
    # 獲取原生的請求體數據[當request對象的其他屬性沒法接受請求體數據時,會把數據保留在data中,如果有request對象的屬性處理了請求體數據,則data就不再保留]
    # print(request.data)  # 如果客戶端上傳的是xml文檔,html格式,二進制流格式,base64格式,就要使用data來接收

    """
    1. 沒有上傳任何數據:
        b''
    2. 上傳json數據
        b'{\n    "username": "xiaoming",\n    "age": 16\n}'
    3. 上傳表單數據
        b''
    4. 上傳xml數據
        b'<goods-list>\n    <goods price="100">\xe5\x95\x86\xe5\x93\x81\xe6\xa0\x87\xe9\xa2\x981</goods>\n    <goods price="200">\xe5\x95\x86\xe5\x93\x81\xe6\xa0\x87\xe9\xa2\x982</goods>\n</goods-list>'
    """

    # 接收表單上傳的數據
    # print(request.form)
    """
    ImmutableMultiDict([('name', '小明'), ('age', '19')])
    """

    # 接收ajax上傳的json數據
    # print(request.json)     # {"username": "xiaoming", "age": 16}
    # print(request.is_json)  # True 往往用于判斷是否是ajax請求

    # 上傳文件列表 HTML必須以<form method="post" enctype="multipart/form-data"> # 表單屬性才能上傳文件
    # print(request.files)
    """
    ImmutableMultiDict([('avatar', <FileStorage: 'avatar.jpg' ('image/jpeg')>)])
    """

    # 接受上傳文件
    # avatar = request.files["avatar"]
    # print(avatar)
    """
    <FileStorage: 'avatar.jpg' ('image/jpeg')>
    from werkzeug.datastructures import FileStorage
    FileStorage,上傳文件處理對象,flask封裝的一個上傳文件處理對象,可以允許我們直接調用對應的方法進行文件的存儲處理, 
    也可以結合其他的ORM模塊像djangoORM那樣通過模型操作對上傳自動存儲處理
    """
    # 處理上傳文件[一般不會這么做!!!而是采用專業的第三方存儲設備來存儲,]
    # from pathlib import Path
    # save_path = str(Path(__file__).parent / "uploads/avatar.jpeg")
    # avatar.save(save_path)

    # 獲取請求頭信息
    print(request.headers)  # 獲取全部的而請求頭信息
    print(request.headers.get("Host"))  # 127.0.0.1:5000,客戶端請求地址,也相當于服務器地址

    # 獲取客戶端發送過來的自定義請求頭
    print(request.headers.get("company"))  # beijing,不存在的鍵的結果:None,存在則得到就是值,
    print(request.headers.get("token"))    # jwt...xxx

    # 獲取客戶端的請求頭中的相關數據
    print(request.user_agent)   # 用戶訪問服務器時使用的網絡代理,一般就是瀏覽器標記信息,PostmanRuntime/7.26.10
    print(request.remote_addr)  # 客戶端遠程地址
    print(request.server)       # 服務端的端點,格式:(IP, 端口)

    # 獲取請求方法
    print(request.method)  # POST

    # 本次請求的url地址
    print(request.url)  # http://127.0.0.1:5000/data
    print(request.root_url)  # 根路徑
    print(request.path)      # /data


    return "獲取請求體"


if __name__ == '__main__':
    app.run()

三、響應

flask默認支持2種響應方式:

數據響應: 默認響應html文本,也可以返回 JSON格式,或其他格式

頁面響應: 重定向

url_for 視圖之間的跳轉

響應的時候,flask也支持自定義http響應狀態碼

3.1、響應html文本

from flask import Flask,make_response, Response

app = Flask(__name__)

app.config.update({
    "DEBUG": True
})


@app.route("/")
def index():
    # 默認返回的就是HTML代碼,在flask內部調用視圖時,得到的返回值會被flask判斷類型,
    # 如果類型不是response對象,則視圖的返回值會被作為response對象的實例參數返回客戶端
    # return "<h1>hello</h1>", 400, {"company": "python"}
    # return make_response("<h1>hello</h1>", 400, {"company": "python"})
    return Response(f"默認首頁", 201, {"company": "python"})


if __name__ == '__main__':
    app.run()

3.2、返回Json數據

在 Flask 中可以直接使用 jsonify 生成一個 JSON 的響應

from flask import Flask, jsonify
from decimal import Decimal
app = Flask(__name__)

app.config.update({
    "DEBUG": True,
    "JSONIFY_PRETTYPRINT_REGULAR": False,
})


@app.route("/")
def index():
    # """返回json格式數據,返回json字典"""
    # data = {"name":"xiaoming","age":16}
    # return data

    # """返回json格式數據,返回各種json數據,包括列表"""
    data = [
        {"id": 1, "username": "liulaoshi", "age": 18},
        {"id": 2, "username": "liulaoshi", "age": 17},
        {"id": 3, "username": "liulaoshi", "age": 16},
        {"id": 4, "username": "小明", "age": Decimal(15)},
    ]
    return jsonify(data)

if __name__ == '__main__':
    app.run()

flask中返回json 數據,都是flask的jsonify方法返回就可以了,直接return只能返回字典格式的json數據。

 3.3、重定向
重定向到站點地址
from flask import Flask, redirect

# 應用實例對象
app = Flask(__name__)

@app.route("/")
def index():
    """頁面跳轉"""
    """
    301: 永久重定向,頁面已經沒有了,站點沒有了,永久轉移了。
    302:臨時重定向,一般驗證失敗、訪問需要權限的頁面進行登錄跳轉時,都是屬于臨時跳轉。
    """
    # redirect函數就是response對象的頁面跳轉的封裝
    # response = redirect("http://www.qq.com", 302)

    # redirect的原理,最終還是借助Resonse對象來實現:
    response = "", 302, {"Location": "http://www.163.com"}
    return response

if __name__ == '__main__':
    # 啟動項目的web應用程序
    app.run(host="0.0.0.0", port=5000, debug=True)

3.3.2、重定向到自己寫的視圖函數

可以直接填寫自己 url 路徑

也可以使用 url_for 生成指定視圖函數所對應的 url

from flask import url_fo

@app.route("/info")
def info():
    return "info"

@app.route("/user")
def user():
    url = url_for("info")
    print(url)
    return redirect(url)

3.3.3、重定向到帶有路徑參數的視圖函數

在url_for函數中傳入參數

from flask import Flask, redirect, url_for

# 應用實例對象
app = Flask(__name__)

@app.route("/demo/<int:mob>")
def mobile(mob):
    print(mob)
    return f"mobile={mob}"

@app.route("/sms")
def sms():
    """攜帶路徑參數進行站內跳轉"""
    # url_for("視圖方法名", 路由路徑參數)
    url = url_for("mobile", mob=13312345678)
    print(url)
    return redirect(url)

if __name__ == '__main__':
    # 啟動項目的web應用程序
    app.run(host="0.0.0.0", port=5000, debug=True)

4、自定義狀態嗎和響應頭

在flask中,可以很方便的返回自定義的狀態碼,以實現不符合http協議的狀態碼,例如:status code 400

from flask import Flask, redirect, url_for, make_response, Response

# 應用實例對象
app = Flask(__name__)

@app.route("/rep")
def rep():
    """常用以下寫法"""
    return "ok", 201, {"Company":"python-35"}

    # """原理"""
    # response = make_response("ok", 201, {"Company": "python-35"})
    # return response
    #
    # """原理"""
    # response = Response("ok")
    # response.headers["Company"] = "oldboy" # 自定義響應頭
    # response.status_code = 201             # 自定義響應狀態碼
    # return response

if __name__ == '__main__':
    # 啟動項目的web應用程序
    app.run(host="0.0.0.0", port=5000, debug=True)

5、http的會話控制

所謂的會話(session),就是客戶端瀏覽器和服務端網站之間一次完整的交互過程.

會話的開始是在用戶通過瀏覽器第一次訪問服務端網站開始.

會話的結束時在用戶通過關閉瀏覽器以后,與服務端斷開.

所謂的會話控制,就是在客戶端瀏覽器和服務端網站之間,進行多次http請求響應之間,記錄、跟蹤和識別用戶的信息而已。

 

為什么要有會話控制?因為 http 是一種無狀態協議,瀏覽器請求服務器是無狀態的。

無狀態:指一次用戶請求時,瀏覽器、服務器無法知道之前這個用戶做過什么,對于服務端而言,客戶端的每次請求都是一次新的請求。

無狀態原因:瀏覽器與服務器是使用 socket 套接字進行通信的,服務器將請求結果返回給瀏覽器之后,會關閉當前的 socket 連接,而且客戶端也會在處理頁面完畢之后銷毀頁面對象。

有時需要保持下來用戶瀏覽的狀態,比如用戶是否登錄過,瀏覽過哪些商品等

實現狀態保持主要有兩種方式:

  • 在客戶端存儲信息使用Cookie(廢棄),token[jwt,oauth]

  • 在服務器端存儲信息使用Session,數據庫

Cookie

Cookie是由服務器端生成,發送給客戶端瀏覽器,瀏覽器會將Cookie的key/value保存,下次請求同一網站時就隨著請求頭自動發送該Cookie給服務器(前提是瀏覽器設置為啟用cookie)。Cookie的key/value可以由服務器端自己定義。

使用場景: 登錄狀態, 瀏覽歷史, 網站足跡,購物車 [不登錄也可以使用購物車]

 

Cookie是存儲在瀏覽器中的一段純文本信息,建議不要存儲敏感信息如密碼,因為電腦上的瀏覽器可能被其它人使用

Cookie基于域名安全,不同域名的Cookie是不能互相訪問的

如訪問fuguang.com時向瀏覽器中寫了Cookie信息,使用同一瀏覽器訪問baidu.com時,無法訪問到fuguang.com寫的Cookie信息,只能獲取到baidu.com的Cookie信息。

瀏覽器的同源策略針對cookie也有限制作用.

當瀏覽器請求某網站時,瀏覽器會自動將本網站下所有Cookie信息隨著http請求頭提交給服務器,所以在request中可以讀取Cookie信息

 

設置cookie

設置cookie需要通過flask的Response響應對象來進行設置,由響應對象會提供了方法set_cookie給我們可以快速設置cookie信息。

@app.route("/set_cookie")
def set_cookie():
    """設置cookie,通過response傳遞到客戶端進行保存"""
    response = make_response('默認首頁')
    response.set_cookie('username', 'xiaoming')            # session會話期有效,關閉瀏覽器后當前cookie就會被刪除
    response.set_cookie('user', 'xiaoming', max_age=30 )   # 指定有效時間,過期以后瀏覽器刪除cookie,max_age=150秒
    return response

獲取cookie

@app.route("/get_cookie")
def get_cookie():
    """獲取來自客戶端的cookie"""
    print(request.cookies)  # ImmutableMultiDict([])
    username = request.cookies.get('username')  # 沒有值則返回None
    user = request.cookies.get('user')          # 沒有值則返回None
    print(f"username={username},user={user}")   # username=xiaoming,user=xiaoming
    return "get cookie"

刪除cookie

@app.route("/del_cookie")
def del_cookie():
    """刪除cookie,重新設置cookie的時間,讓瀏覽器自己根據有效期來刪除"""
    response = make_response('del cookie')
    # 刪除操作肯定是在瀏覽器完成的,所以我們重置下cookie名稱的對飲有效時間為0,此時cookie的值已經不重要了。
    response.set_cookie('user', '', max_age=0)
    response.set_cookie('username', '', max_age=0)
    return response

Session

對于敏感、重要的信息,建議要存儲在服務器端,不能存儲在瀏覽器中,如手機號、驗證碼等信息

在服務器端進行狀態保持的方案就是Session

Session依賴于Cookie,session的ID一般默認通過cookie來保存到客戶端。名字一般叫:sessionid

flask中的session需要加密,所以使用session之前必須配置SECRET_KEY選項,否則報錯.

如果將來希望session的生命周期延長,可以通過修改cookie中的sessionID的有效期來完成配置。

 注意:一般框架都是把session數據保存到服務端,但是,flask里面的session是基于token方式存儲在客戶端的,并沒有安裝傳統的方式保存在服務端的文件中。

 

session的ID存在有效期的,默認是會話期,會話結束了,session_id就廢棄了

設置session

@app.route("/set_session")
def set_session():
    """設置session"""
    session['username'] = 'xiaoming'
    session['info'] = {
        "name": "xiaohong",
        "age": 16,
    }
    return "set_session"

可以通過客戶端瀏覽器中的sessionid觀察,其實默認情況下,flask中的session數據會被加密保存到cookie中的。當然,將來,我們可以采用flask-session第三方模塊把數據轉存到其他的存儲設備,例如:redis或者mysql中。

獲取session

@app.route("/get_session")
def get_session():
    """獲取session"""
    print(session.get('username'))
    print(session.get('info'))
    return "get session"

刪除session

@app.route("/del_session")
def del_session():
    """刪除session,鍵如果不存在,則會拋出異常,所以刪除之前需要判斷鍵是否存在。"""
    if "username" in session:
        session.pop("username")
    if "info" in session:
        session.pop("info")
    return "del_session"

使用過程中,session是依賴于Cookie的,所以當cookie在客戶端被刪除時,對應的session就無法被使用了。