初始化
所有Flask程序都必須創建一個程序實例,Web服務器使用一種名為Web服務器網關接口的的協議(WSGI),把接收自客戶端的所有請求轉發給這個對象處理。程序實例是Flask類的對象,使用下面代碼構建
from flask import Flask
app = FLask(__name__)
Flask類的構造函數只有一個必須指定的參數,即程序主模塊或則包的名稱。。在大多數程序中,Python 的__name__ 變量就是所需的值。將構造函數的name 參數傳給Flask 程序,這一點可能會讓Flask 開發新手心生迷惑。Flask 用這個參數決定程序的根目錄,以便稍后能夠找到相對於程序根目錄的資源文件位置。
路由和視圖函數
客戶端把請求轉發給Web服務器,Web服務器再把請求發送給Flask程序實例。程序實例需要知道對於每個URL請求,運行哪些代碼,所以保存一個URL請求到Python函數的映射關系。處理URL和函數映射關系的程序稱為路由。
在Flask 程序中定義路由的最簡便方式,是使用程序實例提供的app.route 修飾器,把修飾的函數注冊為路由。下面的例子說明了如何使用這個修飾器聲明路由
@app.route("/")
def index():
return "<h1>hello,Flask</h1>"
前例把index() 函數注冊為程序根地址的處理程序。如果部署程序的服務器域名為www.example.com,在瀏覽器中訪問http://www.example.com 后,會觸發服務器執行index() 函數。這個函數的返回值叫做響應,是客戶端接收到的內容。像index這樣的函數稱為視圖函數。視圖函數的返回的響應可以是簡單的html,也可以是復雜的表單。
如果你仔細觀察日常所用服務的某些URL 格式,會發現很多地址中都包含可變部分,Flask 支持這種形式的URL,只需在route 修飾器中使用特殊的句法即可。下例定義的路由中就有一部分是動態名字
@app.route("/user/<name>")
def index(name):
return "<h1>hello,%s</h1>" % name
路由中的動態部分默認使用字符串,不過也可使用類型定義。例如,路由/user/int:id只會匹配動態片段id 為整數的URL。Flask 支持在路由中使用int、float 和path 類型。path 類型也是字符串,但不把斜線視作分隔符,而將其當作動態片段的一部分。
啟動服務器
程序實例用run 方法啟動Flask 集成的開發Web 服務器:
if __name == '___main_':
app.run(debug=True)
name=='main' 是Python 的慣常用法,在這里確保直接執行這個腳本時才啟動開發Web 服務器。如果這個腳本由其他腳本引入,程序假定父級腳本會啟動不同的服務器,因此不會執行app.run()。
服務器啟動后,會進入輪詢,等待並處理請求。輪詢會一直運行,直到程序停止,比如按Ctrl-C 鍵
有一些選項參數可被app.run() 函數接受用於設置Web 服務器的操作模式。在開發過程中啟用調試模式會帶來一些便利,比如說激活調試器和重載程序。要想啟用調試模式,我們可以把debug 參數設為True。
一個完整的程序
前幾節介紹了Flask Web 程序的不同組成部分,現在是時候開發一個程序了。整個hello.py程序腳本就是把前面介紹的三部分合並到一個文件中。
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return '<h1>hello,flask</h1>'
if __name__ == '__main__':
app.run(debug=True)
之后激活虛擬環境,然后執行python hello.py,打印一下日志
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 240-501-696
在瀏覽器輸入http://127.0.0.1:5000/,可以看到結果輸出。
請求−響應循環
下面介紹一下flask框架的設計理念
程序和請求上下文
Flask 從客戶端收到請求時,要讓視圖函數能訪問一些對象,這樣才能處理請求。請求對象就是一個很好的例子,它封裝了客戶端發送的HTTP 請求。要想讓視圖函數能夠訪問請求對象,一個顯而易見的方式是將其作為參數傳入視圖函數,不過這會導致程序中的每個視圖函數都增加一個參數。除了訪問請求對象,如果視圖函數在處理請求時還要訪問其他對象,情況會變得更糟.為了避免大量可有可無的參數把視圖函數弄得一團糟,Flask 使用上下文臨時把某些對象
變為全局可訪問。有了上下文,就可以寫出下面的視圖函數:
from flask import request
@app.route("/")
def index():
user_agent=request.headers.get('User_Agent')
return '<p>Your browser is %s</p>' % user_agent
注意在這個視圖函數中我們如何把request 當作全局變量使用。事實上,request 不可能是全局變量。試想,在多線程服務器中,多個線程同時處理不同客戶端發送的不同請求時,每個線程看到的request 對象必然不同。Falsk 使用上下文讓特定的變量在一個線程中全局可訪問,與此同時卻不會干擾其他線。
在Flask 中有兩種上下文:程序上下文和請求上下文:
- 程序上下文
- current_app:當前激活程序的程序實例
- g:處理請求時用作臨時存儲的對象。每次請求都會重設這個變量
- 請求上下文
- request:請求對象,封裝了客戶端發出的HTTP 請求中的內容
- session:用戶會話,用於存儲請求之間需要“記住”的值的詞典
請求調度
程序收到客戶端發來的請求時,要找到處理該請求的視圖函數。為了完成這個任務,Flask會在程序的URL 映射中查找請求的URL。URL 映射是URL 和視圖函數之間的對應關系。Flask 使用app.route 修飾器或者非修飾器形式的app.add_url_rule() 生成映射。
要想查看Flask 程序中的URL 映射是什么樣子,我們可以在Python shell 中檢查為hello.py生成的映射。測試之前,請確保你激活了虛擬環境。
>>> from hello import app
>>> app.url_map
Map([<Rule '/' (HEAD, OPTIONS, GET) -> index>,
<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>])
>>>
/ 路由在程序中使用app.route 修飾器定義。/static/
URL 映射中的HEAD、Options、GET 是請求方法,由路由進行處理。Flask 為每個路由都指定了請求方法,這樣不同的請求方法發送到相同的URL 上時,會使用不同的視圖函數進行處理。HEAD 和OPTIONS 方法由Flask 自動處理,因此可以這么說,在這個程序中,URL映射中的3 個路由都使用GET 方法。
請求鈎子
有時在處理請求之前或之后執行代碼會很有用。例如,在請求開始時,我們可能需要創建數據庫連接或者認證發起請求的用戶。為了避免在每個視圖函數中都使用重復的代碼,Flask 提供了注冊通用函數的功能,注冊的函數可在請求被分發到視圖函數之前或之后調用。Flask支持一下四種鈎子:
- before_first_request:注冊一個函數,在處理第一個請求之前運行
- before_request:注冊一個函數,在處理每一個請求的時候運行
- after_request:注冊一個函數,如果沒有未處理的異常拋出,在每次請求之后運行
- teardown_request:注冊一個函數,即使有未處理的異常拋出,也在每次請求之后運行
在請求鈎子函數和視圖函數之間共享數據一般使用上下文全局變量g。例如,before_request 處理程序可以從數據庫中加載已登錄用戶,並將其保存到g.user 中。隨后調用視圖函數時,視圖函數再使用g.user 獲取用戶,鈎子的具體用法會在后面詳細介紹。
響應
Flask 調用視圖函數后,會將其返回值作為響應的內容。大多數情況下,響應就是一個簡單的字符串,作為HTML 頁面回送客戶端。但HTTP 協議需要的不僅是作為請求響應的字符串。HTTP 響應中一個很重要的部分是狀態碼,Flask 默認設為200,這個代碼表明請求已經被成功處理。
如果視圖函數返回的響應需要使用不同的狀態碼,那么可以把數字代碼作為第二個返回值,添加到響應文本之后。例如,下述視圖函數返回一個400 狀態碼,表示請求無效
@app.route(/)
def index():
return '<p>bad request</p>',400
視圖函數返回的響應還可接受第三個參數,這是一個由首部(header)組成的字典,可以添加到HTTP 響應中。如果不想返回由1 個、2 個或3 個值組成的元組,Flask 視圖函數還可以返回Response 對象。make_response() 函數可接受1 個、2 個或3 個參數(和視圖函數的返回值一樣),並返回一個Response 對象。有時我們需要在視圖函數中進行這種轉換,然后在響應對象上調用各種方法,進一步設置響應。下例創建了一個響應對象,然后設置了cookie:
from flask import make_response
@app.route('/')
def index():
response = make_response('<h1>This document carries a cookie!</h1>')
response.set_cookie('answer', '42')
return response
有一種名為重定向的特殊響應類型。這種響應沒有頁面文檔,只告訴瀏覽器一個新地址用以加載新頁面.重定向經常使用302 狀態碼表示,指向的地址由Location 首部提供。重定向響應可以使用3 個值形式的返回值生成,也可在Response 對象中設定。不過,由於使用頻繁,Flask 提供了redirect() 輔助函數,用於生成這種響應:
from flask import redirect
@app.route('/')
def index():
return redirect('http://www.example.com')
還有一種特殊的響應由abort 函數生成,用於處理錯誤。在下面這個例子中,如果URL 中動態參數id 對應的用戶不存在,就返回狀態碼404:
from flask import abort
@app.route('/user/<id>')
def get_user(id):
user = load_user(id)
if not user:
abort(404)
return '<h1>Hello, %s</h1>' % user.name
注意,abort 不會把控制權交還給調用它的函數,而是拋出異常把控制權交給Web 服務器