如今你已經玩過一個基本的Flask應用程序,你也許想要知道更多關於Flask如何施展魔力。下面章節描述了一些框架設計方面的特色。python
當Flask從客戶端收到一個請求,它須要提供幾個可用對象給視圖函數處理。request對象是個不錯的例子,它封裝了客戶端發送的HTTP請求。git
Flask視圖函數訪問request對象的最好方式,就是做爲一個參數發送它,但這須要每一個單一視圖函數在應用程序中有一個額外的參數。考慮一下,若是request對象不是惟一一個視圖函數須要訪問完成請求的對象,事情將會變得更加複雜。github
爲了不弄亂視圖函數那些可能須要或不須要的參數,Flask使用context來臨時肯定可訪問的全局對象。也多虧了context,視圖函數能夠寫成下面這樣:web
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中的不一樣對象。contexts使得Flask肯定可訪問的全局變量而不干擾其餘線程。shell
注:線程是能夠獨立管理的最小指令序列。一個進程中有多個活動的線程是很是常見的,有時分享內存或文件句柄資源。多線程web服務器會啓動一個線程池並從池中選擇一個線程來處理每一個傳入的請求。數據庫
Flask有兩類context:應用級context 和 請求級context。表2-1展現了這些context提供的變量。flask
表2-1. Flask全局context瀏覽器
Flask激活(或壓棧)應用級context和請求級context在調度請求以前,而後刪除他們當請求被處理後。當應用程序context被壓入棧,線程中current_app
和g
變量變得可用;一樣的,當請求級context被壓入棧,request
和session
變量也一樣變得可用。若是這些變量中的任何一個不是由激活的應用級或請求級context訪問,會產生錯誤。在後面的章節會詳細討論四個context變量,因此不要擔憂你不理解它們的用處。服務器
下面的Python shell會話演示了應用級context是如何工做的:cookie
>>> from hello import app >>> from flask import current_app >>> current_app.name Traceback (most recent call last): ... RuntimeError: working outside of the application context >>> app_ctx = app.app_context() >>> app_ctx.push() >>> current_app.name 'hello' >>> app_ctx.pop()
在這個示例中,當應用級context沒有激活,可是卻做爲有效的context被壓入棧中,current_app.name
報錯。注意在應用程序實例中一個應用級context是如何經過調用app.app_context()
來得到的。
當一個應用程序收到客戶端的請求,它須要找到響應的視圖函數爲之服務。對於這個任務,Flask會在應用程序的URL映射中查找請求的URL,該映射包含URLs和操做它們的視圖函數。Flask經過app.route
裝飾器或非裝飾器版本app.add_url_rule()
來創建這個映射。
看一下Flask應用程序中URL映射是怎樣的,你能夠在Python shell中檢查hello.py
建立的映射。測試中,請確保你的虛擬環境是激活狀態:
(venv) % python >>> from hello import app >>> app.url_map Map([<Rule '/' (HEAD, OPTIONS, GET) -> index>, <Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>, <Rule '/user/<name>' (HEAD, OPTIONS, GET) -> user>])
/
和/user/<name>
路由是由應用程序中的app.route
所定義。/static/<filename>
路由是由Flask添加,用於訪問靜態文件的一個特殊路由。你將在第三章學習更多關於靜態文件的內容。
URL映射中所示的HEAD
、OPTIONS
、GET
元素爲request方法,由路由處理。Flask鏈接方法到每一個路由,這樣不一樣的請求方法發送到相同的URL能夠被不一樣的視圖函數處理。HEAD
和OPTIONS
方法由Flask自動管理,因此實際上能夠說,在這個應用程序中URL映射的三個路由都鏈接到GET
方法了。在第四章你將學習爲路由指定不一樣的請求方法。
有些時候在每一個請求處理以前或以後執行代碼是很是有用的。例如,在開始每個請求前可能有必要建立數據庫鏈接,或對用戶請求進行驗證。爲了不復制處理這些操做的代碼到每個視圖函數中,Flask給你選擇註冊相同函數來調用,在請求被分配給視圖函數以前或以後。
請求hooks由裝飾器實現。下面是四個Flask支持的hooks:
before_first_request
:在第一個請求被處理前註冊一個函數運行。before_request
:在每個請求前註冊一個函數運行。after_request
:若是沒有未處理的異常發生,在每個請求後註冊一個函數運行。teardown_request
:即便未處理的異常發生,在每個請求後註冊一個函數運行。在請求hook函數和視圖函數之間共享數據的慣用方法就是使用g
全局context。例如,before_request
處理程序能夠從數據庫加載已登陸的用戶並保存在g.user
中。以後,當視圖函數被調用,能夠從那訪問用戶。
請求hooks的示例會在將來的章節中展現給你們,因此不用擔憂,
當Flask調用一個視圖函數,並指望它的返回值去響應該請求。大多數的響應是將簡單字符串構成的HTML頁面發回給客戶端。
可是HTTP協議須要比字符串更多的信息做爲請求的響應。一個HTTP響應中很是重要的部分是狀態碼,Flask默認設置200來指示請求已經成功處理。
當視圖函數須要用不一樣的狀態碼響應,能夠在響應文本後添加數字碼做爲第二個返回值。例如,下面的視圖函數返回400錯誤狀態碼的請求:
@app.route('/') def index(): return '<h1>Bad Request</h1>', 400
視圖函數返回的響應還能夠攜帶第三個參數,添加一個頭部字典給HTTP響應。一般不多用到,可是你能夠在第十四章看到示例。
除了返回一個、兩個或三個值的元組,Flask視圖函數能夠選擇返回response對象。make_response()
函數可攜帶一個、兩個或三個參數,和視圖函數返回的值同樣,並返回一個response對象。有時候在視圖函數中執行這個轉換是很是有用的,而後使用response對象中的方法進一步配置響應。下面的示例建立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
有一類特殊的響應稱做重定向。這類響應不包含頁面文檔;只是給瀏覽器一個新的URL去加載新的頁面。重定向一般和web表單一塊兒使用,你將在第四章學習。
重定向一般由302響應狀態碼註明而且重定向的URL由頭部的Location
給出。重定向響應可使用三個值的返回生成,也可經過響應對象生成,可是鑑於它頻繁的使用,Flask提供redirect()
函數來建立這樣的響應:
from flask import redirect @app.route('/') def index(): return redirect('http://www.example.com')
另外一個具備中斷功能的特殊響應用來錯誤處理。下面的示例,當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
注意終止不是指將控制權返回給調用它的函數,而是指經過拋出異常將控制權返回給web服務。
Flask是可擴展的。它故意騰出地給重要的功能,例如數據庫和用戶受權,給你自由去選擇最適合你的應用程序的包,或寫一個本身想要的。
社區開發了很是多的擴展用於各類用途,若是這還不夠,可使用任何Python標準包和庫。爲了讓你瞭解一個擴展是如何併入一個應用程序的,下面的章節給hello.py添加一個擴展,增長應用程序的命令行參數。
Flask開發,其web服務器支持一系列的啓動配置選項,可是配置它們的惟一方式只有在腳本中傳遞參數給app.run()
並調用。這不是很是的方便,理想方法是經過命令行參數傳遞配置選項。
Flask-Script是給你的Flask應用程序添加命令行解釋的擴展。它打包了一組通用的選項,還支持自定義命令。
使用pip安裝擴展:
(venv) $ pip install flask-script
示例2-3展現了在 hello.py 應用程序中添加命令行解釋的變化。
示例2-3. hello.py:使用Flask-Script
from flask.ext.script import Manager manager = Manager(app) # ... if __name__ == '__main__': manager.run()
專爲Flask開發的擴展暴露在flask.ext
命名空間下。Flask-Script從flask.ext.script
中導出一個名爲Manager
的類。
初始化這個擴展的方法和其餘許多擴展同樣:主類實例的初始化是經過將應用程序實例做爲參數傳遞給構造函數實現的。建立的對象適當的用於每個擴展。在這個示例中,服務器啓動經過manager.run()
來路由,且命令行在這被解析。
建議:若是你有克隆在GitHub上的應用程序,你如今能夠運行
git checkout 2c
來切換到這個版本的應用程序。
由於這些變化,應用程序得到一組基本的命令行選項。運行hello.py
顯示可用信息:
$ python hello.py usage: hello.py [-h] {shell, runserver} ... positional arguments: {shell, runserver} shell 在Flask應用程序上下文的內部運行一個Python Shell。 runserver 運行Flask開發服務器,例如:app.run() optional arguments: -h, --help 顯示這個幫助信息並退出
shell
命令用於在應用程序上下文中啓動一個Python shell會話。你可使用這個會話去運行維護任務,或測試,或調試錯誤。
runserver
命令,就像它的名稱同樣,啓動web服務。運行python hello.py runserver
在調試模式下啓動web服務,還有更多的選項:
(venv) $ python hello.py runserver --help usage: hello.py runserver [-h] [-t HOST] [-p PORT] [--threaded] [--processes PROCESSES] [--passthrough-errors] [-d] [-r] 運行Flask開發服務器,例如:app.run() optional arguments: -h, --help 顯示這個幫助信息並退出 -t HOST, --host HOST -p PORT, --port PORT --threaded --processes PROCESSES --passthrough-errors -d, --no-debug -r, --no-reload
--host
參數是一個很是有用的選項,由於它能告訴web服務器監聽哪一個網絡接口的客戶端鏈接。默認,Flask開發的web服務器監聽localhost
的鏈接,因此只有來自內部計算機運行的服務器能夠接收。下面的命令使得web服務器監聽公網接口,其餘網絡上的計算機能夠鏈接:
(venv) $ python hello.py runserver --host 0.0.0.0 * Running on http://0.0.0.0:5000/ * Restarting with reload
如今web服務器應該能夠從網絡中的任何一臺計算機訪問 http://a.b.c.d:5000 ,「a.b.c.d」是運行服務的計算機的外部IP地址。
這一章介紹了請求響應的概念,但說的更多的是響應。Flask使用模板爲生成響應提供很是好的支持,這是很是重要的話題,下一章會重點講它。