一、Flask 簡(jiǎn)介
Flask是一個(gè)使用 Python 編寫的輕量級(jí) Web 應(yīng)用框架。其 WSGI 工具箱采用 Werkzeug ,模板引擎則使用 Jinja2 。Flask使用 BSD 授權(quán)。
Flask也被稱為 “microframework” ,因?yàn)樗褂煤?jiǎn)單的核心,并且被設(shè)計(jì)為可擴(kuò)展形式。故而沒(méi)有提供一些重要的功能,例如數(shù)據(jù)庫(kù)和用戶認(rèn)證,所以開(kāi)發(fā)者可以自由選擇最適合程序的包,或者按照需求自行開(kāi)發(fā)。
社區(qū)成員開(kāi)發(fā)了大量的不同用途的擴(kuò)展,如果這還不能滿足需求,你還可以使用所有Python標(biāo)準(zhǔn)包或代碼庫(kù)。

二、Flask 安裝
一般安裝python后會(huì)自帶pip工具,方便下載安裝python各類庫(kù),如果沒(méi)有自行百度。
pip install flask
三、最簡(jiǎn)單Flask應(yīng)用
# app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return "Hello World!"
if __name__ == '__main__':
app.run() # 啟動(dòng)服務(wù)器
寫完后直接用python運(yùn)行,默認(rèn)監(jiān)聽(tīng)本地5000端口,訪問(wèn)http://127.0.0.1:5000/就能看到Hello World!
四、初始化應(yīng)用
所有Flask程序都必須創(chuàng)建一個(gè)程序?qū)嵗?/code>,所有功能都是圍繞這個(gè)實(shí)例進(jìn)行的,接收自客戶端的所有請(qǐng)求都將轉(zhuǎn)交到這個(gè)對(duì)象處理。程序?qū)嵗荈lask類的對(duì)象,經(jīng)常使用下述代碼創(chuàng)建一個(gè)實(shí)例:
from flask import Flask
app = Flask(__name__)
五、路由和視圖函數(shù)
-
路由
客戶端(如Web瀏覽器)把請(qǐng)求發(fā)送給Web服務(wù)器,Web服務(wù)器再把請(qǐng)求發(fā)送給Flask程序?qū)嵗绦驅(qū)嵗枰缹?duì)每個(gè)URL請(qǐng)求運(yùn)行哪些代碼,所以保存了一個(gè)URL到Python函數(shù)的映射關(guān)系。處理URL和函數(shù)之間關(guān)系的程序稱為路由。
-
視圖函數(shù)
前面最簡(jiǎn)單的Flask應(yīng)用中定義了一個(gè)函數(shù)hello_world(),該函數(shù)被注冊(cè)為程序根地址(/)的處理程序,也就是訪問(wèn)http://127.0.0.1:5000/時(shí),會(huì)觸發(fā)服務(wù)器執(zhí)行hello_world()函數(shù),函數(shù)的返回值成為響應(yīng),是客戶端接收到的內(nèi)容,如果客戶端是Web瀏覽器,響應(yīng)就是返回給用戶的HTML文檔。
像hello_world()這樣的用于處理請(qǐng)求的函數(shù)稱為視圖函數(shù)。視圖函數(shù)可以返回簡(jiǎn)單的HTML字符串,也可以返回一個(gè)完整的HTML文檔(這里就要用到Jinja2模板引擎啦)。
- 注冊(cè)一個(gè)視圖函數(shù)最常用的方法就是用
app.route():
@app.route("/")
def hello_world():
pass
- 限制路由的請(qǐng)求方法
@app.route("/", methods=["GET"])
def hello_world():
pass
這樣,要是客戶端以POST的方式請(qǐng)求該路由,就會(huì)返回“405 Method Not Allowed”
- 向路由函數(shù)傳參
有的時(shí)候我們需要通過(guò)url獲取有用的信息,比如說(shuō)知道來(lái)訪者的姓名,以便在返回的歡迎語(yǔ)中加入他的名字。
@app.route("/hello/<string:name>")
def hello(name):
return "hello, " + name
上例中,在定義該路由的訪問(wèn)路徑里加了一個(gè)<string:name>,并增加了一個(gè)同名函數(shù)參數(shù)。
更一般的情況是<type:param>,其中type是類型,常用的有string、int,param就是參數(shù)名,它會(huì)傳遞到路由函數(shù)的同名參數(shù)里。
六、啟動(dòng)服務(wù)器
當(dāng)Flask應(yīng)用被初始化且視圖函數(shù)定義好之后,就可以啟動(dòng)Flask應(yīng)用啦。
if __name__ == "__main__":
app.run() # 默認(rèn)監(jiān)聽(tīng)本地5000端口
# app.run(port=8080) 監(jiān)聽(tīng)8080端口
七、請(qǐng)求 - 響應(yīng)循環(huán)
說(shuō)到請(qǐng)求,必須要先介紹一下上下文
-
上下文(Context)
上下文,在閱讀文章時(shí)經(jīng)常提到,意思是語(yǔ)境、語(yǔ)意。在程序設(shè)計(jì)中,上下文的概念也是類似的,你可以這么理解:
通俗地講,每一段代碼都有許多外部變量,比如一個(gè)函數(shù)需要傳入若干個(gè)必填的參數(shù)才能夠運(yùn)行,那些參數(shù)就是上下文的一部分,意味著該函數(shù)無(wú)法獨(dú)立運(yùn)行,需要上下文的支持。而許多函數(shù)都是需要各種參數(shù)才能運(yùn)行的,這些參數(shù)組成的集合就稱為上下文。
-
Flask中的上下文
| 變量名 | 上下文 | 說(shuō)明 |
|---|---|---|
| current_app | 程序上下文 | 當(dāng)前激活程序的程序?qū)嵗?/td> |
| g | 程序上下文 | 上下文全局變量 |
| request | 請(qǐng)求上下文 | 請(qǐng)求對(duì)象,封裝了客戶端發(fā)出的HTTP請(qǐng)求中的內(nèi)容 |
| session | 請(qǐng)求上下文 | 用戶會(huì)話,用戶儲(chǔ)存同一個(gè)客戶端在多個(gè)請(qǐng)求間需要“記住”的信息 |
-
請(qǐng)求上下文
請(qǐng)求上下文變量是在視圖函數(shù)被觸發(fā)時(shí)傳入的一個(gè)HTTP請(qǐng)求對(duì)象,儲(chǔ)存了所有客戶端發(fā)送過(guò)來(lái)的數(shù)據(jù),因此該變量只有在視圖函數(shù)中才能訪問(wèn)。如果難以理解,你只需要記住request變量只有在視圖函數(shù)中才能訪問(wèn)
我們可以通過(guò)以下方式引入請(qǐng)求上下文變量:
from flask import request
-
請(qǐng)求上下文變量常用的訪問(wèn)操作有:
request.args 獲取所有GET請(qǐng)求參數(shù)
request.form 獲取所有form-data、x-www-form-urlencoded類型的請(qǐng)求參數(shù)
request.json 獲取所有json類型的請(qǐng)求參數(shù)
request.method 獲取請(qǐng)求方法
request.headers 獲取請(qǐng)求頭 -
請(qǐng)求鉤子
什么是鉤子(Hook)?
打個(gè)比方,魚鉤是用來(lái)釣魚的,一旦魚咬了鉤,鉤子就一直鉤住魚了,任憑魚在水里怎么游,也逃不出魚鉤的控制。在程序設(shè)計(jì)里,鉤子就是一個(gè)事件,鉤住了某段代碼,任憑這段代碼這么運(yùn)行,都逃不過(guò)我的鉤子事件。-
Flask里的請(qǐng)求鉤子
鉤子 說(shuō)明 before_first_request 注冊(cè)一個(gè)函數(shù),在處理第一個(gè)請(qǐng)求之前運(yùn)行 before_request 注冊(cè)一個(gè)函數(shù),在每次請(qǐng)求之前運(yùn)行 after_request 注冊(cè)一個(gè)函數(shù),如果沒(méi)有未處理的異常拋出,在每次請(qǐng)求之后運(yùn)行 teardown_request 注冊(cè)一個(gè)函數(shù),即使有未處理的異常拋出,也在每次請(qǐng)求之后運(yùn)行 用法
from flask import request @app.before_request def app_before_request(): print("HTTP {} {}".format(request.method, request.url))這樣在每次請(qǐng)求時(shí)都會(huì)輸出請(qǐng)求方式和請(qǐng)求url,但其實(shí)鉤子函數(shù)的作用遠(yuǎn)不止這些。
-
響應(yīng)
通常,每個(gè)視圖函數(shù)都有返回值,而最簡(jiǎn)單的就是一串字符串了,F(xiàn)lask默認(rèn)返回的類型是text/html,狀態(tài)碼為200。狀態(tài)碼很重要,如果需要修改,我們可以這樣做:
@app.route("/") def hello_word(): return "Not Found", 404當(dāng)然,如果要自定義響應(yīng),最好還是引入make_response函數(shù),該函數(shù)接受一個(gè)bytes對(duì)象(二進(jìn)制數(shù)據(jù))或者字符串作為響應(yīng)主體,并返回一個(gè)響應(yīng)對(duì)象response,通過(guò)這個(gè)對(duì)象我們可以自定義該響應(yīng)的頭部,比如修改響應(yīng)頭部的Content-Type和Content-Disposition來(lái)告訴客戶端這個(gè)文件是需要被下載的,并且將From設(shè)置為Ncuhome來(lái)告訴客戶端該文件來(lái)自家園工作室。
@app.route("/download") def download(): response = make_response(open("lesson.md", "rb").read()) response.headers["From"] = "Ncuhome" response.headers["Content-Type"] = "application/octet-stream; charset=utf-8;" response.headers["Content-Disposition"] = "attachment;filename=lesson.md" return response -
快速構(gòu)建一個(gè)json格式的響應(yīng)
前后端交互時(shí),一般都是使用JSON格式數(shù)據(jù)進(jìn)行交互
from flask import jsonify @app.route('/hello', methods=['GET']) def user(user_id): if not user_id == 1: abort(404) return jsonify({ "status": 1, "data": { "username": "ncuhome", "desc": "Hello, Ncuhomer!", }, "message": "獲取成功" }) -
其他一些響應(yīng)
- 重定向
from flask import redirect @app.route("/ncuos") def ncuos(): return redirect("https://www.ncuos.com/")- 重定向到某個(gè)路由
from flask import url_for @app.route("/redirect_to_hello") def redirect(): return redirect(url_for("hello")) @app.route("/hello") def hello(): return "Hello, Ncuhomer!"- 返回HTTP狀態(tài)碼
from flask import abort @app.route("/user/<int:user_id>") def get_user(user_id): abort(404)
Jinja2 模板引擎
前面提到了Jinja2,它的作用其實(shí)是為了方便構(gòu)造HTML文檔、以及其他的一些文檔內(nèi)容(郵件等)。
- 響應(yīng)返回完整HTML
簡(jiǎn)單舉個(gè)栗子吧
模板文件 ./template/index.html
其中name就是我們需要填補(bǔ)的,它用花括號(hào){{ }}括起來(lái)了。<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Ncuhome Demo</title> </head> <body> Hello, {{ name }} </body> </html>
填補(bǔ)name并返回完整HTML:from flask import render_template @app.route("/hello/<string:name>") def hello(name): return render_template("index.html", name=name) - 模板中的for循環(huán)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Ncuhome Demo</title> </head> <body> {% for user in users %} {{ loop.index }}: {{ user }} <br> {% endfor %} </body> </html>from flask import render_template @app.route("/users") def hello(name): users = ["Amy", "David", "Sam"] return render_template("index.html", users=users) - 模板中的if
if和for類似,都擁有一個(gè)作用域,用法也一樣。{% if name %} {{ name }} {% endif %}