(Flask Web開發:基於Python的Web應用開發實戰)------學習筆記(第2章)
第2章 程式的基本結構
本章將帶你瞭解 Flask 程式各部分的作用,編寫並執行第一個 Flask Web 程式。
2.1 初始化
所有 Flask 程式都必須建立一個程式例項,程式例項是 Flask 類的物件。
Web 伺服器使用一種名為 Web 伺服器閘道器介面(Web Server Gateway Interface,WSGI)的協議,把接收自客戶端的所有請求都轉交給這個物件處理。經常使用下述程式碼建立:
from flask import Flask
app = Flask(__name__)
Flask 類的建構函式只有一個必須指定的引數,即程式主模組或包的名字__name__。不能傳入整形值會報錯,也不能傳入標準模組名,會影響靜態檔案的訪問,不會影響檢視函式的訪問。
__name__的作用:
1>Flask 用這個引數決定程式的根目錄,以便稍後能夠找到相對於程式根目錄的資原始檔位置。
2>建立靜態路由http://127.0.0.1:5000/static/,方便靜態資源的訪問。
2.2 路由和檢視函式
客戶端(例如 Web 瀏覽器)把請求傳送給 Web 伺服器,Web 伺服器再把請求傳送給 Flask程式例項。程式例項需要知道對每個 URL 請求執行哪些程式碼,所以儲存了一個 URL 到Python 函式的對映關係。
路由:處理 URL 和函式之間關係的程式。
在 Flask 程式中,使用程式例項提供的 app.route 修飾器定義路由,把修飾的函式註冊為路由。下面的例子說明了如何使用這個修飾器宣告路由:
@app.route('/')
def index():
return '<h1>Hello World!</h1>'
檢視函式和響應:前例把 index() 函式註冊為程式根地址的處理程式。當瀏覽器訪問了部署程式的伺服器,會觸發伺服器執行 index() 函式。這個函式的返回值稱為響應,是客戶端接收到的內容。如果客戶端是 Web 瀏覽器,響應就是顯示給使用者檢視的文件。像 index() 這樣的函式稱為檢視函式(view function)。檢視函式返回的響應可以是包含HTML 的簡單字串,也可以是複雜的表單。
某些 URL 格式會包含可變部分。尖括號中的內容就是動態部分,任何能匹配靜態部分的 URL 都會對映到這個路由上。呼叫檢視函式時,Flask 會將動態部分作為引數傳入函式。
路由中的動態部分預設使用字串
path 型別:是字串,但不把斜線視作分隔符,而將其當作動態片段的一部分。
@app.route('/user/<name>')
def user(name):
return '<h1>Hello, %s!</h1>' % name
2.3 啟動伺服器
程式例項用 run 方法啟動 Flask 整合的開發 Web 伺服器(flask提供的伺服器不適合開發模式下使用):app.run( )函式可以接收設定web伺服器操作模式的引數:除錯模式 -> debug=True:
if __name__ == '__main__':
app.run(debug=True)
name==’ main ':
用來判斷程入口,在這裡確保直接執行這個指令碼時才啟動開發Web 伺服器。如果這個指令碼由其他指令碼引入,__name__顯示的值為模組名。
2.4 一個完整的程式
程式程式碼如示例 2-1 所示。
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return '<h1>Hello World!</h1>'
@app.route('/')
def user('/user/<name>):
return '<h1>Hello, %s!</h1>' % name
if __name__ == '__main__':
app.run(debug=True)
2.5 請求-響應迴圈
現在你已經開發了一個簡單的 Flask 程式,下面幾節將介紹這個框架的一些設計理念。
2.5.1 程式和請求上下文
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 程式執行過程中的一些資訊。
請求上下文變數,如下表所示:
變數 | 內容 |
---|---|
request | 封裝了HTTP請求的內容,針對的是http請求。user = request.args.get(‘user’),獲取的是get請求的引數。 |
session | 用來記錄請求會話中的資訊,實現狀態保持的設定,針對的是使用者資訊。session.get(‘name’)獲取使用者資訊。 |
程式上下文變數,如下表所示:
變數 | 內容 |
---|---|
g | 處理請求時用作臨時儲存的物件。每次請求都會重設這個變數 |
current_app | 當前啟用程式的程式例項,用來記錄程式執行過程中的配置資訊,如記錄專案的專案日誌 |
注意:Flask 在分發請求之前啟用(或推送)程式和請求上下文,請求處理完成後再將其刪除。程式上下文被推送後,就可以線上程中使用 current_app 和 g 變數。request 和 session 變數。如果使用這些變數時我們沒有啟用程式上下文或請求上下文,就會導致錯誤。在程式例項上呼叫 app.app_context() 可獲得一個程式上下文。
2.5.2 請求排程
程式收到客戶端發來的請求時,要找到處理該請求的檢視函式。為了完成這個任務,Flask會在程式的 URL 對映中查詢請求的 URL。Flask 使用 app.route 修飾器或者非修飾器形式的 app.add_url_rule() 生成對映。 要想檢視 Flask 程式中的 URL 對映是什麼樣子,我們可以在 Python shell 中檢查為 hello.py生成的對映。
URL 對映: URL 和檢視函式之間的對應關係。
print(app.url_map)
---------------------------------------------------------
Map([<Rule '/' (HEAD, OPTIONS, GET) -> index>,
<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
<Rule '/user/<name>' (HEAD, OPTIONS, GET) -> user>])
注意:URL 對映中的 HEAD、Options、GET 是請求方法,由路由進行處理。Flask 為每個路由都指定了請求方法,這樣不同的請求方法傳送到相同的 URL 上時,會使用不同的檢視函式進行處理。HEAD 和 OPTIONS 方法由 Flask 自動處理,三個路由預設使用GET方法。可以在修飾器中新增請求方法:@app.route(’/’,[“GET”,“POST”])
2.5.3 請求鉤子
有時在處理請求之前或之後執行程式碼會很有用。例如,在請求開始時,我們可能需要建立資料庫連線或者認證發起請求的使用者。Flask 提供了註冊通用函式的功能,註冊的函式可在請求被分發到檢視函式之前或之後呼叫。
請求鉤子使用修飾器實現。Flask 支援以下 4 種鉤子。
鉤子 | 內容 |
---|---|
before_first_request | 註冊一個函式,在處理第一個請求之前執行。 |
before_request | 註冊一個函式,在每次請求之前執行。 |
after_request | 註冊一個函式,如果沒有未處理的異常丟擲,在每次請求之後執行。 |
teardown_request | 註冊一個函式,即使有未處理的異常丟擲,也在每次請求之後執行。 |
注意:在請求鉤子函式和檢視函式之間共享資料一般使用上下文全域性變數 g。例如,before_request 處理程式可以從資料庫中載入已登入使用者,並將其儲存到 g.user 中。隨後呼叫檢視函式時,檢視函式再使用 g.user 獲取使用者。
2.5.4 響應
Flask 呼叫檢視函式後,會將其返回值作為響應的內容。大多數情況下,響應就是一個簡單的字串,作為 HTML 頁面回送客戶端。但 HTTP 協議需要的不僅是作為請求響應的字串。HTTP 響應中一個很重要的部分是狀態碼。
(1)根據需求返回不同的狀態碼:如果檢視函式返回的響應需要使用不同的狀態碼,那麼可以把數字程式碼作為第二個返回值,添到響應文字之後。示例程式如下:
@app.route('/')
def index():
return '<h1>Bad Request</h1>', 400
(2)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
(3)將重定向的作為檢視函式的返回型別。這種響應沒有頁面文件,只告訴瀏覽器一個新地址用以載入新頁面。重定向經常在 Web 表單中使用。重定向經常使用 302 狀態碼錶示,指向的地址由 Location 首部提供。Flask 提供了 redirect() 輔助函式,用於生成這種響應:
from flask import redirect
@app.route('/')
def index():
return redirect('http://www.example.com')
由 abort 函式生成的響應,abort函式:flask中的異常處理語句。abort函式只能丟擲符合http協議的異常狀態碼,在下面這個例子中,如果 URL 中動態引數 id 對應的使用者不存在,就返回狀態碼 404:
作用:abort函式一般用來實現自定義的錯誤資訊,讓程式碼的擴充套件性更好,提高使用者體驗。
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 伺服器。只要觸發abort後面的程式碼不會執行。
2.6 Flask擴充套件
Flask 被設計為可擴充套件形式,故而沒有提供一些重要的功能,例如資料庫和使用者認證,所以開發者可以自由選擇最適合程式的包,或者按需求自行開發。接下來我們以在 hello.py 中新增一個擴充套件,使用命令列引數增強程式的功能為例。使用Flask-Script支援命令列選項,Flask 的開發 Web 伺服器支援很多啟動設定選項,但只能在指令碼中作為引數傳給app.run()函式。
Flask-Script :一個 Flask 擴充套件,為 Flask 程式添加了一個命令列解析器。Flask-Script 自帶了一組常用選項,而且還支援自定義命令。Flask-Script 擴充套件使用 pip 安裝:pip install flask-script
示例程式:
from flask import Flask
from flask_script import Manager
app = Flask(__name__)
manager = Manager(app)
@app.route('/')
def index():
return '<h1>Hello World!</h1>'
if __name__ == '__main__':
manager.run()
檢視命令列的終端命令:python hello.py runserver --help
啟動伺服器的終端命令:python3 hello.py runserver -p 5000
代替app呼叫run方法:在python直譯器的配置的script parameters中必須加入runserver
作用:可以在終端手動傳入ip和port,可以自定義指令碼引數,可以實現資料庫的遷移
–host 引數是個很有用的選項,它告訴 Web 伺服器在哪個網路介面上監聽來自客戶端的
連線。預設情況下,Flask 開發 Web 伺服器監聽 localhost 上的連線,所以只接受來自服
務器所在計算機發起的連線。
下述命令讓 Web 伺服器監聽公共網路介面上的連線,允許同
網中的其他計算機連線伺服器:
(1)python hello.py runserver --host 0.0.0.0
(2)Running on http://0.0.0.0:5000/
(3)Restarting with reloader
現在,Web 伺服器可使用 http://a.b.c.d:5000/ 網路中的任一臺電腦進行訪問,其中“a.b.c.d”
是伺服器所在計算機的外網 IP 地址。