瞭解了WSGI架構,我們發現:其實一個Web App,就是寫一個WSGI的處理函數,針對每個HTTP請求進行響應。
但是如何處理HTTP請求不是問題,問題是如何處理100個不同的URL。
每一個URL可以對應GET和POST請求,當然還有PUT、DELETE等請求,但是我們通常只考慮最常見的GET和POST請求。
一個最簡單的想法是從environ變數裡取出HTTP請求的資訊,然後逐個判斷:
def application(environ, start_response): method = environ['REQUEST_METHOD'] path = environ['PATH_INFO'] if method=='GET' and path=='/': return handle_home(environ, start_response) if method=='POST' and path='/signin': return handle_signin(environ, start_response) ...
只是這麼寫下去代碼是肯定沒法維護了。
代碼這麼寫沒法維護的原因是因為WSGI提供的介面雖然比HTTP介面進階了不少,但和Web App的處理邏輯比,還是比較低級,我們需要在WSGI介面之上能進一步抽象,讓我們專註於用一個函數處理一個URL,至於URL到函數的映射,就交給Web架構來做。
由於用Python開發一個Web架構十分容易,所以Python有上百個開源的Web架構。這裡我們先不討論各種Web架構的優缺點,直接選擇一個比較流行的Web架構——Flask來使用。
用Flask編寫Web App比WSGI介面簡單(這不是廢話麼,要是比WSGI還複雜,用架構幹嘛?),我們先用easy_install或者pip安裝Flask:
$ easy_install flask
然後寫一個app.py,處理3個URL,分別是:
- GET /:首頁,返回Home;
- GET /signin:登入頁,顯示登入表單;
- POST /signin:處理登入表單,顯示登入結果。
注意噢,同一個URL/signin分別有GET和POST兩種請求,映射到兩個處理函數中。
Flask通過Python的裝飾器在內部自動地把URL和函數給關聯起來,所以,我們寫出來的代碼就像這樣:
from flask import Flaskfrom flask import requestapp = Flask(__name__)@app.route('/', methods=['GET', 'POST'])def home(): return 'Home
'@app.route('/signin', methods=['GET'])def signin_form(): return ''''''@app.route('/signin', methods=['POST'])def signin(): # 需要從request對象讀取表單內容: if request.form['username']=='admin' and request.form['password']=='password': return 'Hello, admin!
' return 'Bad username or password.
'if __name__ == '__main__': app.run()
運行python app.py,Flask內建的Server在連接埠5000上監聽:
$ python app.py * Running on http://127.0.0.1:5000/Try
開啟瀏覽器,輸入首頁地址http://localhost:5000/:
首頁顯示正確!
再在瀏覽器地址欄輸入http://localhost:5000/signin,會顯示登入表單:
輸入預設的使用者名稱admin和口令password,登入成功:
輸入其他錯誤的使用者名稱和口令,登入失敗:
實際的Web App應該拿到使用者名稱和口令後,去資料庫查詢再比對,來判斷使用者是否能登入成功。
除了Flask,常見的Python Web架構還有:
- Django:全能型Web架構;
- web.py:一個小巧的Web架構;
- Bottle:和Flask類似的Web架構;
- Tornado:Facebook的開源非同步Web架構。
當然了,因為開發Python的Web架構也不是什麼難事,我們後面也會自己開發一個Web架構。
小結
有了Web架構,我們在編寫Web應用時,注意力就從WSGI處理函數轉移到URL+對應的處理函數,這樣,編寫Web App就更加簡單了。
在編寫URL處理函數時,除了配置URL外,從HTTP請求拿到使用者資料也是非常重要的。Web架構都提供了自己的API來實現這些功能。Flask通過request.form['name']來擷取表單的內容。