快速啟動(dòng)
是不是很渴望馬上開(kāi)始???這篇文檔將會(huì)很好的向你介紹Flask。假設(shè)你已經(jīng)安裝好了Flask。如果還沒(méi)有安裝的話,
請(qǐng)查看Installation部分。
一個(gè)小型的應(yīng)用
一個(gè)很小型的Flask應(yīng)用是像下面這樣的:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
上面的代碼做了什么呢?
1. 首先,我們引入了**Flask**類(lèi),創(chuàng)建這個(gè)類(lèi)的一個(gè)實(shí)例,這個(gè)實(shí)例將會(huì)成為我們的WSGI應(yīng)用。
2. 接下來(lái),我們創(chuàng)建了這個(gè)類(lèi)的實(shí)例,第一個(gè)參數(shù)是這個(gè)應(yīng)用的包名或者模塊名。
如果你只是使用了一個(gè)模塊(就像這個(gè)例子一樣),你應(yīng)該使用**__name__**作為參數(shù),
因?yàn)楦鶕?jù)模塊是作為程序運(yùn)行,或者是作為包被引入,它的名字是變化的('__main__' versus the actual import name),
這個(gè)是必須的,因?yàn)镕lask由此知道去哪里尋找模板、靜態(tài)文件等等。更多信息請(qǐng)查看[Flask](http://flask.pocoo.org/docs/0.12/api/#flask.Flask)
3. 我們使用**route()**裝飾器去告訴Flask,什么樣的URL觸發(fā)哪一個(gè)函數(shù)。
4. 該函數(shù)的給出一個(gè)名稱,也生成該特定函數(shù)的URL,并返回我們要在用戶瀏覽器中顯示的消息。
以 hello.py 保存這個(gè)文件,或者其他類(lèi)似的名字也可以,確保它的文件名不是 flask.py ,因?yàn)檫@樣會(huì)和Flask自己產(chǎn)生沖突。
為了運(yùn)行這個(gè)程序,一種方法是使用flask命令,另一種方法是python's -m switch with Flask.
在這之前,你需要設(shè)置環(huán)境變量FLASK_APP,以此來(lái)告訴你的終端需要運(yùn)行的程序是哪個(gè)。
export FLASK_APP = hello.py
flask run
* Running on http://127.0.0.1:5000/
如果你的事Windows系統(tǒng),你需要使用set代替export。
另一種方法,你也可以使用python -m flask:
export FLASK_APP = hello.py
python -m flask run
* Running on http://127.0.0.1:5000/
這樣就建立了一個(gè)很簡(jiǎn)單的內(nèi)置服務(wù)器,足以測(cè)試使用了,但是不能用于生產(chǎn)環(huán)境。
關(guān)于部署相關(guān)的信息,請(qǐng)查看Deployment Options
現(xiàn)在請(qǐng)轉(zhuǎn)向http://127.0.0.1:5000/,你將會(huì)看到自己的hello world問(wèn)候!
外部可訪問(wèn)的服務(wù)器
如果你運(yùn)行上面的服務(wù),你會(huì)發(fā)現(xiàn)僅僅你自己的電腦可以訪問(wèn)這個(gè)服務(wù)器,互聯(lián)網(wǎng)上的其他電腦不能訪問(wèn)。
這是默認(rèn)值,因?yàn)樵谡{(diào)試模式下,應(yīng)用程序的用戶可以在計(jì)算機(jī)上執(zhí)行任意Python代碼。
如果你的調(diào)試模式是關(guān)閉的,或者你相信你網(wǎng)絡(luò)內(nèi)的用戶,你可以通過(guò)如下代碼使服務(wù)器在遠(yuǎn)程網(wǎng)絡(luò)內(nèi)可見(jiàn):
flask run --host=0.0.0.0
這將會(huì)告訴你的系統(tǒng),監(jiān)聽(tīng)所有公用的IP地址。
如果服務(wù)沒(méi)有啟動(dòng)怎么辦?
如果python -m flask失敗,或者flask不存在,可能會(huì)有多種原因?qū)е碌摹?br> 首先,你需要查看錯(cuò)誤信息。
舊版本的Flask
在0.11版本之前的Flask,會(huì)有不同的啟動(dòng)應(yīng)用的方式。簡(jiǎn)單地說(shuō),flask命令行不存在,
python -m flask也一樣不存在。在這樣的情況下,你有兩種選擇:一種是升級(jí)到新版本的Flask,
另一種是查看Development Server
文檔,選擇一個(gè)可用的啟動(dòng)方法。
無(wú)效的導(dǎo)入名稱
FLASK_APP環(huán)境變量是在flask run的時(shí)候,需要導(dǎo)入的模塊名稱。
如果模塊名稱不正確,您將在啟動(dòng)時(shí)收到導(dǎo)入錯(cuò)誤(或者當(dāng)您導(dǎo)航到應(yīng)用程序時(shí)啟用調(diào)試)。
它會(huì)告訴你它在嘗試引入什么,為什么發(fā)生錯(cuò)誤了。
最常見(jiàn)的錯(cuò)誤是打字錯(cuò)誤,或者沒(méi)有創(chuàng)建應(yīng)用對(duì)象。
調(diào)試模式
(想要記錄錯(cuò)誤和堆棧跟蹤?請(qǐng)參閱Applications Errors)
flask腳本可以很好的啟動(dòng)一個(gè)本地的開(kāi)發(fā)環(huán)境,但是當(dāng)你的代碼有改動(dòng)的時(shí)候,你必須手動(dòng)重新啟動(dòng)服務(wù)器。
這樣就感覺(jué)不方便,F(xiàn)lask可以做的更好。
如果啟用了調(diào)試支持,代碼更改以后服務(wù)器將自動(dòng)重新載入代碼,如果出現(xiàn)問(wèn)題,它還將為您提供一個(gè)有用的調(diào)試器。
你可以在運(yùn)行前,通過(guò)設(shè)置FLASK_APP環(huán)境變量來(lái)啟動(dòng)調(diào)試模式。
$ export FLASK_APP=1
$ flask run
(在Windows環(huán)境下,需要使用set代替export)
上面代碼做了如下事情:
- 激活了調(diào)試器;
- 激活了自動(dòng)重新加載;
- 打開(kāi)了Flask應(yīng)用的調(diào)試模式。
在Development Server文檔中詳細(xì)解釋了更多的參數(shù)。
注意
即使交互式調(diào)試器在分支環(huán)境中不起作用(這使得幾乎不可能在生產(chǎn)服務(wù)器上使用)。
它仍然允許執(zhí)行任意代碼。這使得它成為主要的安全風(fēng)險(xiǎn),因此絕不能在生產(chǎn)環(huán)境上使用。
下面是一個(gè)調(diào)試器的截圖:
Hava another debugger in ming? see Working with Debubbers
路由
現(xiàn)代的移動(dòng)應(yīng)用程序都有美觀的URL地址。這便于人們記憶,這對(duì)于使用較慢網(wǎng)絡(luò)連接的移動(dòng)設(shè)備的應(yīng)用來(lái)說(shuō)尤其如此。
如果用戶可以直接進(jìn)入所需的頁(yè)面,而不必打索引頁(yè)面,則更有可能他們會(huì)喜歡該頁(yè)面,并且下次再來(lái)。
就像你在上面看見(jiàn)的,路由裝飾器route()的作用就是把一個(gè)函數(shù)綁定到一個(gè)URL地址。
下面是一些基本的例子:
@app.route('/')
def index():
return 'Index Page'
@app.route('/hello')
def hello():
return 'Hello, World!'
當(dāng)然,不僅僅如此!您可以使URL的某些部分動(dòng)態(tài),并將多個(gè)規(guī)則附加到一個(gè)函數(shù)。
變量規(guī)則
向URL中添加變量,你可以這樣標(biāo)記 <variable_name> 。
這個(gè)部分會(huì)以關(guān)鍵字的形式出入到你的函數(shù)中,你也可以選擇使用類(lèi)型聲明 <converter:variable_name>
。下面是一些比較好的例子:
@app.route('/user/<username>')
def show_user_profile(username):
# show the user profile for that user
return 'User %s' % username
@app.route('/post/<int:post_id>')
def show_post(post_id):
# show the post with the given id, the id is an integer
return 'Post %d' % post_id
有以下的類(lèi)型轉(zhuǎn)換:
|name | descriptions |
|:-------|-----------------------------------------------:|
|string | accepts any text without a shash (the default) |
|int | accepts integers |
|float | like _int_ but for floating point values |
|path | like the default but also accepts slashes |
|any | mathches one of the items provided |
|uuid | accepts UUID strings |
特殊的URL/重定向行為
Flask的URL規(guī)則基于Werkzeug的路由模塊。他的這個(gè)模塊背后的想法是確?;贏pache和早期的HTTP服務(wù)器規(guī)定的先例的漂亮而唯一的URL。
看一下下面兩個(gè)規(guī)則:
@app.route('/projects/')
def projects():
return 'The project page'
@app.route('/about')
def about():
return 'The about page'
雖然他們看起來(lái)很像,但是區(qū)別在于URL定義時(shí)使用的斜杠 / 。在第一個(gè)URL定義中,
projects地址的末尾有一個(gè) / 。這就類(lèi)似于文件系統(tǒng)中的一個(gè)文件夾。
當(dāng)你嘗把斜杠去掉的時(shí)候,它會(huì)重定向到帶斜杠的URL。
在第二個(gè)示例中,URL末尾是沒(méi)有斜杠的,而是像類(lèi)UNIX系統(tǒng)上的文件的路徑名。使用尾部斜杠訪問(wèn)該URL將產(chǎn)生404“未找到”錯(cuò)誤。
這種行為允許相對(duì)URL繼續(xù)工作,即使省略尾部斜線,與Apache和其他服務(wù)器的工作原理一致。
此外,URL將保持唯一,這有助于搜索引擎避免對(duì)同一頁(yè)面進(jìn)行兩次索引。
構(gòu)建URL
如果可以匹配URL,是否Flask也可以生成它們?回答是肯定的。你可以使用url_for()函數(shù)把一個(gè)URL綁定到特定的
視圖函數(shù)。它允許函數(shù)名作為第一個(gè)參數(shù),和其他的一些關(guān)鍵字參數(shù),每一個(gè)對(duì)應(yīng)于URL中的可變部分。
未知的變量部分作為查詢參數(shù)附加到URL。下面是一些例子:
from flask import Flask, url_for
app = Flask(__name__)
@app.route('/')
def index(): pass
@app.route('/login')
def login(): pass
@app.route('/user/<username>')
def profile(username): pass
with app.test_request_context():
print (url_for('index'))
print (url_for('login'))
print (url_for('login', next='/'))
print (url_for('profile', username='John Doe'))
輸出如下:
/
/login
/login?next=/
/user/John%20Doe
這里使用test_request_context()方法,解釋如下。它告訴Flask像處理請(qǐng)求一樣,
即使我們使用的是Python shell。請(qǐng)查看詳細(xì)描述Context Locals
為什么要使用URL構(gòu)建函數(shù) url_for() 來(lái)構(gòu)建URL,而不是將它們直接寫(xiě)到模板中?
這樣做有3個(gè)好處:
- Reversing通常比直接硬編碼更具有可讀性。更重要的是,它允許您一次更改URL,而無(wú)需記住所有需要更改URL。
- URL構(gòu)建將為您透明地處理特殊字符和Unicode數(shù)據(jù)的轉(zhuǎn)義,因此您不必處理它們。
- 如果您的應(yīng)用程序位于URL根目錄之外 - 例如,/myapplication 而不是 / , url_for() 將會(huì)很好的處理他們。
HTTP方法
HTTP(Web應(yīng)用程序直間進(jìn)行溝通的協(xié)議)知道用于訪問(wèn)URL的不同方法。默認(rèn)的,一個(gè)路由僅僅對(duì)GET請(qǐng)求進(jìn)行回應(yīng)。
但是你可以通過(guò)向route()裝飾器增加methods參數(shù)來(lái)改變。下面是一些示例:
from flask import request
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
do_the_login()
else:
show_the_login_form()
如果是GET請(qǐng)求,HEAD會(huì)自動(dòng)為你加上,你不用自己進(jìn)行處理。也會(huì)確保請(qǐng)求頭HEAD按照HTTP RFC(the document describing the HTTP protocol)
的要求進(jìn)行處理。和Flask0.6一樣,OPTIONS也會(huì)自動(dòng)為你實(shí)現(xiàn)。
你是不是不知道什么是HTTP方法???不必?fù)?dān)心,這里有一個(gè)HTTP方法的快速介紹,并且解釋為什么它們很重要:
GET
瀏覽器告訴服務(wù)器只需獲取存儲(chǔ)在該頁(yè)面上的信息并發(fā)送它。這可能是最常用的方法。
HEAD
瀏覽器告訴服務(wù)器獲取信息,但它只對(duì)頁(yè)眉感興趣,而不是頁(yè)面的內(nèi)容。一個(gè)應(yīng)用程序應(yīng)該可以處理這樣的請(qǐng)求,就像接到一個(gè)GET請(qǐng)求,
但是不返回實(shí)際的內(nèi)容。在Flask中,你一點(diǎn)兒也不需要為此擔(dān)心,底層的Werkzeug庫(kù)會(huì)為你做這些。
POST
瀏覽器告訴服務(wù)器,它想向URL中推送一些新的信息,并且服務(wù)器需要保證數(shù)據(jù)被保存了,而且只保存了一次。
這是一種HTML表單(forms)經(jīng)常向服務(wù)器傳送數(shù)據(jù)的方式。
PUT
和POST相似,但服務(wù)器可能會(huì)多次觸發(fā)存儲(chǔ)過(guò)程來(lái)覆蓋舊值。也許你會(huì)疑問(wèn),為什么需要這個(gè)呢?但是這樣做確實(shí)有一些好處。
假如在傳輸數(shù)據(jù)的時(shí)候連接斷開(kāi)了,在這種情況下,瀏覽器和服務(wù)器之間的系統(tǒng)可能會(huì)第二次安全地接收請(qǐng)求,而不會(huì)破壞事件。
如果使用的是POST請(qǐng)求的話,就沒(méi)法實(shí)現(xiàn),因?yàn)橹粫?huì)被觸發(fā)一次。
DELETE
刪除在特定位置的信息。
OPTIONS
為客戶提供一個(gè)快速的方法來(lái)確定此URL支持哪些方法。從Flask 0.6開(kāi)始,這是為您自動(dòng)實(shí)現(xiàn)的。
靜態(tài)文件
動(dòng)態(tài)的互聯(lián)網(wǎng)應(yīng)用也需要靜態(tài)的文件。通常也是存儲(chǔ)CSS和JavaScript文件的地方。
理想情況下,您的Web服務(wù)器被配置為為您服務(wù),但在開(kāi)發(fā)期間,F(xiàn)lask也可以執(zhí)行此操作。
只需在程序包中或在模塊旁邊創(chuàng)建一個(gè)名為static的文件夾,它將在應(yīng)用程序的 /static 處可用。
如果為靜態(tài)文件創(chuàng)建一個(gè)URL地址,使用特殊的static端點(diǎn)名稱:
url_for('static', filename='style.css')
這個(gè)文件必須被保存在目錄static/style.css的文件系統(tǒng)下。
模板渲染
在Python中生成HTML不是很有趣的事情,實(shí)際上很麻煩,因?yàn)槟仨氉孕袌?zhí)行HTML轉(zhuǎn)義以保護(hù)應(yīng)用程序的安全。
鑒于此,F(xiàn)lask默認(rèn)使用Jinjia2模板引擎。
你可以使用render_template()方法渲染一個(gè)模板。你所需要做的僅僅是提供一個(gè)模板的名字以及想要傳入模板的一組變量名。
這組變量名是以字典的形式傳入的。下面是一個(gè)簡(jiǎn)單的示例,如何渲染模板:
from flask import render_template
@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
return render_template('hello.html', name=name)
Flask將會(huì)在templates文件夾內(nèi)尋找模板。所以,如果你的程序是一個(gè)模塊的話,該文件夾位于模塊的旁邊,
如果他是一個(gè)包的話,該文件夾位于包內(nèi)。
示例1:模塊
/application.py
/templates
/hello.html
示例2:包
/application
/__init__.py
/templates
/hello.html
對(duì)于模板,你可以使用Jinjia2模板的全部功能。請(qǐng)到官網(wǎng)Jinjia2 Template Documentation
獲取更多信息。
下面是一個(gè)示例模板:
<!doctype html>
<title>Hello from Flask</title>
{% if name %}
<h1>Hello {{name}}</h1>
{%else%}
<h1>Hello, World!</h1>
{%endif%}
在模板內(nèi)部,你也可以使用request, session和g對(duì)象,還有get_flashed_messages()函數(shù)。
在模板中使用繼承很重要,如果你想知道繼承是怎么工作的,請(qǐng)點(diǎn)擊Template Inheritance
文檔?;镜哪0謇^承可以使每一個(gè)頁(yè)面有相同的標(biāo)題、導(dǎo)航、頁(yè)腳。
模板具有自動(dòng)轉(zhuǎn)譯的功能,如果name內(nèi)板涵HTML內(nèi)容,那么它將會(huì)被自動(dòng)轉(zhuǎn)譯。如果你相信一個(gè)變量(例如因?yàn)樗鼇?lái)自將wiki標(biāo)記轉(zhuǎn)換為HTML的模塊),并且你知道它是安全的
HTML內(nèi)容,那么你可以使用Markup類(lèi)進(jìn)行標(biāo)記,或者你也可以在模板內(nèi)使用 |safe 過(guò)濾器。
請(qǐng)到Jinjia2文檔獲得更多信息。
下面簡(jiǎn)單介紹Markup類(lèi)如何工作的:
from flask import Markup
Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>'
# Markup(u'<strong>Hello <blink>hacker</blink>!</strong>')
Markup.escape('<blink>hacker</blink>')
# Markup(u'<blink>hacker</blink>')
Markup('<em>Marked up</em> ? HTML').striptags()
# u'Marked up \xbb HTML'
版本0.5的變化:所有模板不再啟用自動(dòng)轉(zhuǎn)譯。模板的以下擴(kuò)展名觸發(fā)自動(dòng)轉(zhuǎn)義:.html,.htm,.xml,.xhtml。從字符串加載的模板將禁用自動(dòng)轉(zhuǎn)義。
獲取請(qǐng)求數(shù)據(jù)
對(duì)于Web應(yīng)用程序,對(duì)客戶端發(fā)送到服務(wù)器的數(shù)據(jù)做出反應(yīng)至關(guān)重要。在Flask中,這些信息由全局變量request對(duì)象提供。
如果你對(duì)Python有了解,也許會(huì)有疑問(wèn),為什么它可以是全局的(因?yàn)镻ython沒(méi)有聲明的全局、局部變量),并且Flask怎么
保證的線程安全。答案書(shū)本地上下文環(huán)境(context locals):
本地上下文(context locals)
內(nèi)幕消息(Insider Information)
如果您想了解這些工作原理以及如何使用上下文本地實(shí)現(xiàn)測(cè)試,請(qǐng)閱讀本節(jié),否則只需跳過(guò)該部分。
在Flask內(nèi)有一些固定的全局對(duì)象,但是不是常規(guī)所說(shuō)的那樣的全局對(duì)象。
這些對(duì)象實(shí)際上是對(duì)特定上下文對(duì)象的哦代理。太拗口了?。。〉珜?shí)際上這很容易理解。
想象一下一個(gè)正在處理線程的上下文,這時(shí)一個(gè)新的請(qǐng)求進(jìn)來(lái)了,web服務(wù)器覺(jué)定新開(kāi)一個(gè)線程(或其他的方式,底層對(duì)象能夠處理除線程之外的并發(fā)系統(tǒng)),
當(dāng)Flask啟動(dòng)其內(nèi)部請(qǐng)求處理時(shí),它會(huì)找出當(dāng)前線程活動(dòng)的上下文,并將當(dāng)前應(yīng)用程序和WSGI環(huán)境綁定到該上下文(線程)。
它以一種智能的方式執(zhí)行,以便一個(gè)應(yīng)用程序可以調(diào)用另一個(gè)應(yīng)用程序而不會(huì)中斷。
所以,這對(duì)于你來(lái)說(shuō)意味著什么?基本上你完全可以忽視,除非你在做某些事情,如單元測(cè)試的時(shí)候。您將注意到,由于沒(méi)有請(qǐng)求對(duì)象,依賴于請(qǐng)求對(duì)象的代碼將突然中斷。
解決方案是自己創(chuàng)建一個(gè)請(qǐng)求對(duì)象并將其綁定到上下文。對(duì)于單元測(cè)試來(lái)說(shuō),最簡(jiǎn)單的方法是使用test_request_context()方法。
結(jié)合with語(yǔ)句,它將綁定一個(gè)測(cè)試請(qǐng)求,以便您可以與它進(jìn)行交互。下面是一個(gè)示例:
from flask import request
with app.test_request_context('/hello', method='POST'):
# now you can do something with the request until the
# end of the with block, such as basic assertions:
assert request.path == '/hello'
assert request.method == 'POST'
另一種可能性是將整個(gè)WSGI環(huán)境傳遞給 request_context() 方法:
from flask import request
with app.request_context(environ):
assert request.method == 'POST'
請(qǐng)求對(duì)象(Request Object)
請(qǐng)求對(duì)象在API部分中有講述,我們將不在此詳細(xì)介紹,參加request
這是一些最常見(jiàn)的操作的概述。首先,你需要從 flask 模塊中引入:
from flask import request, render_template
使用method屬性,可以獲取當(dāng)前的請(qǐng)求方法,你可以使用form屬性獲取表單數(shù)據(jù)(以 POST 和 PUT 請(qǐng)求發(fā)送的數(shù)據(jù))
以下是上述兩個(gè)屬性的完整示例:
from flask import request
@app.route('/login', methods=['POST', 'GET'])
def login():
error = None
if request.method == 'POST':
if valid_login(request.form['username'], request.form['password']):
return log_the_user_in(request.form['username'])
else:
error = 'Invalid username/password'
# the code below is executed if the request mentod
# was GET or the credentials were invalid
return render_template('login.html', error=error)
如果 form 屬性中沒(méi)有所尋找的關(guān)鍵字,將會(huì)發(fā)生什么?在這種情況下,會(huì)拋出一個(gè)KeyError錯(cuò)誤。
你可以像捕捉標(biāo)準(zhǔn)錯(cuò)誤一樣對(duì)它進(jìn)行捕獲,如果你沒(méi)有捕獲的話,則會(huì)出現(xiàn)一個(gè) HTTP404 錯(cuò)誤請(qǐng)求頁(yè)面。
所以在許多情況下,你不必處理這個(gè)問(wèn)題。
如果想獲取在UTL(?key=value)中提交的參數(shù),你可以使用args屬性:
from flask import request
searchword = request.args.get('key', '')
我們建議使用 get 方法獲取URL參數(shù),或者通過(guò)捕獲KeyError
為你用戶可能輸入錯(cuò)的URL,這時(shí)候,如果返回 400 的錯(cuò)誤請(qǐng)求頁(yè)面會(huì)顯得不友好。
如果想查看request對(duì)象的全部方法和屬性,請(qǐng)點(diǎn)擊request.
上傳文檔
使用Flask,你可以輕易的處理文件的上傳。請(qǐng)確保在你的HTML表單里設(shè)置 enctype='multipart/form-data' 屬性。
否則,瀏覽器不會(huì)發(fā)送你的文件滴。
待上傳的文件存儲(chǔ)在文件系統(tǒng)的內(nèi)存或臨時(shí)位置。你可以通過(guò)查看request對(duì)象里的files屬性,來(lái)獲得這些文件。
每一個(gè)待上傳的文件都是放在這個(gè)字典里。它就像一個(gè)標(biāo)準(zhǔn)的Python文件對(duì)象一樣,但它也有一個(gè) save() 方法,可以將該文件存儲(chǔ)在服務(wù)器的文件系統(tǒng)上。
下面是一個(gè)簡(jiǎn)單的示例,展示它是如何工作的:
from flask import request
@app.route('/upload', methods=['POST', 'GET'])
def upload_file():
if request.method == 'POST':
f = request.files['the_file']
f.save('/var/www/uploads/upload_file.txt')
如果您想知道文件在客戶端上傳到應(yīng)用程序之前如何命名,你可查看filename屬性。
但是,請(qǐng)記住,這個(gè)值是可以被偽造的,永遠(yuǎn)不會(huì)相信這個(gè)價(jià)值。如果要使用客戶端的文件名將文件存儲(chǔ)在服務(wù)器上,請(qǐng)使用
Werkzeug提供給你的secure_filemame()函數(shù),示例如下:
from flask import request
from werkzeug import secure_filename
@app.route('/upload', methods=['POST', 'GET'])
def upload_file():
if request.method == 'POST':
f = request.files['the_file']
f.save('/var/www/uploads/' + secure_filename(f.filename))
想看更好的例子,請(qǐng)查閱Uploading Files
Cookies
可以使用cookies屬性獲取cookies。
可以使用response對(duì)象的[set_cookie]方法設(shè)置cookies。request對(duì)象的cookies屬性
是一個(gè)包含所有用戶傳遞的cookies的字典。如果你想使用sessions,,就不要直接使用cookies,請(qǐng)使用Flask提供的Sessions
在cookies的基礎(chǔ)上增加了安全性。
示例,讀cookies:
from flask import request
@app.route('/')
def index():
username = request.cookies.get('username')
# use cookies.get(key) instead of cookies[key] to not get a
# KeyError if the cookies is missing.
示例,保存cookies:
from flask import make_response, render_template
@app.route('/')
def index():
resp = make_response(render_template(...))
resp.set_cookie('username', 'the username')
return resp
請(qǐng)記住,是在response對(duì)象上設(shè)置cookies。因?yàn)槟阃ǔV皇菑囊晥D函數(shù)返回字符串,F(xiàn)lask將會(huì)將它們轉(zhuǎn)換為響應(yīng)對(duì)象。
如果您明確要這樣做,可以使用make_response函數(shù),然后修改它。
有時(shí)您可能想要在響應(yīng)對(duì)象不存在的位置設(shè)置一個(gè)cookie。這可以使用Defered Request Callbacks模式來(lái)實(shí)現(xiàn)。
這一部分請(qǐng)查閱About Response
重定向和錯(cuò)誤(Redirects and Errors)
要將用戶重定向到另一個(gè)端點(diǎn),請(qǐng)使用redirect函數(shù).
要使用錯(cuò)誤代碼提前中止請(qǐng)求,請(qǐng)使用abort函數(shù):
from flask import abort, redirect, url_for
@app.route('/')
def index():
return redirect(url_for('login'))
@app.route('/login')
def login():
abort(401)
this_is_never_executed()
這是一個(gè)相當(dāng)無(wú)關(guān)的例子,因?yàn)橛脩魧⒈粡乃饕囟ㄏ虻綗o(wú)法訪問(wèn)的頁(yè)面(401意味著拒絕訪問(wèn)),僅僅展示如何使用。
默認(rèn)情況下,每個(gè)錯(cuò)誤代碼都會(huì)顯示黑白錯(cuò)誤頁(yè)面。如果你想自己設(shè)置錯(cuò)誤頁(yè)面,你可以使用errorhandler()裝飾器。
from flask import render_template
@app.route(404)
def page_not_found(error):
return render_template('page_not_found.html'), 404
請(qǐng)注意在函數(shù)render_template后面的 404 。
這告訴Flask該頁(yè)面的狀態(tài)代碼應(yīng)該是404,這意味著沒(méi)有找到。默認(rèn)情況下,200意味著:一切順利。
更多細(xì)節(jié),請(qǐng)查看Error handler.
關(guān)于響應(yīng)
視圖函數(shù)的返回值將自動(dòng)轉(zhuǎn)換為響應(yīng)對(duì)象。如果返回值是一個(gè)字符串,則將其轉(zhuǎn)換為響應(yīng)對(duì)象,其中字符串作為響應(yīng)體,
200 OK狀態(tài)代碼和text / html mimetype。Flask將返回值轉(zhuǎn)換為響應(yīng)對(duì)象所使用的的邏輯如下:
- 如果返回正確類(lèi)型的響應(yīng)對(duì)象,則從視圖直接返回。
- 如果它是一個(gè)字符串,則使用該數(shù)據(jù)和默認(rèn)參數(shù)創(chuàng)建一個(gè)響應(yīng)對(duì)象。
- 如果返回一個(gè)元組,元組中的項(xiàng)可以提供額外的信息。這樣的元組形式必須是(response, status, headers) or (response, headers),元組中必須至少有一個(gè)元素。
狀態(tài)值將覆蓋狀態(tài)代碼,標(biāo)題可以是附加標(biāo)題值的列表或字典。 - 如果以上情況都沒(méi)有,則Flask將假定返回值是一個(gè)有效的WSGI應(yīng)用程序,并將其轉(zhuǎn)換為響應(yīng)對(duì)象。
如果您想要在視圖中獲取生成的響應(yīng)對(duì)象,你可以使用make_response函數(shù)。
假設(shè)你有下面這個(gè)視圖函數(shù):
@app.errorhandler(404)
def not_found(error):
return render_template('error.html'), 404
您只需要使用make_response()包裝返回表達(dá)式,并獲取響應(yīng)對(duì)象進(jìn)行修改,然后返回:
@app.errorhandler(404)
def not_found(error):
resp = make_response(render_template('error.html'), 404)
resp.headers['X-Something'] = 'A value'
return resp
會(huì)話(sessions)
除了請(qǐng)求對(duì)象之外,還有一個(gè)名為session的第二個(gè)對(duì)象,它允許您將特定于用戶的信息從一個(gè)請(qǐng)求存儲(chǔ)到下一個(gè)請(qǐng)求。
這是在您的cookies之上實(shí)現(xiàn)的,并且用密碼標(biāo)識(shí)cookie。這意味著用戶可以查看您的cookie的內(nèi)容,但不修改它,除非他們知道用于簽名的密鑰。
為了使用會(huì)話,你必須設(shè)置一個(gè)密鑰。下面展示會(huì)話是如何工作的:
from flask import Flask, session, redirect, url_for, escape, request
app = Flask(__name__)
@app.route('/')
def index():
if 'username' in session:
return 'Logged in as %s' escape(session['username'])
return 'You are not logged in.'
@app.route('/login', methods=['POST', 'GET'])
def login():
if request.method == 'POST':
session['username'] = request.form['username']
return redirect(url_for('index'))
return '''
<form method='post'>
<p><input type=text name=username>
<p><input type=submit value=login>
</form>
'''
@app.route('logout')
def logout():
# remove the username from the session if it's there
session.pop('username', None)
return redirect(url_for('index'))
# set the secret key. keep this really secret.
app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
如果您不使用模板引擎(如本例所示),這里的escape()會(huì)為您轉(zhuǎn)義。
如何生成好的密鑰
隨機(jī)的問(wèn)題是很難判斷真正的隨機(jī)性。密鑰應(yīng)盡可能隨機(jī).您的操作系統(tǒng)有方法根據(jù)加密隨機(jī)生成器生成漂亮的隨機(jī)東西,可以用于獲取這樣的密鑰:
import os
os.urandom(24)
基于Cookie的會(huì)話的提示:Flask將會(huì)取得您放入會(huì)話對(duì)象的值并將其序列化,放入cookie。
如果您發(fā)現(xiàn)某些值不會(huì)在請(qǐng)求中持久存在,而你確實(shí)啟用了Cookie,并且您沒(méi)有收到明確的錯(cuò)誤消息,請(qǐng)檢查您的頁(yè)面響應(yīng)中的cookie大小與Web瀏覽器支持的大小相比。
除了默認(rèn)的基于客戶端的會(huì)話之外,如果要在服務(wù)器端處理會(huì)話,還有幾個(gè)Flask擴(kuò)展支持此功能。
消息閃爍(Message Flashing)
良好的應(yīng)用程序和用戶界面都是關(guān)于反饋的。如果用戶沒(méi)有得到足夠的反饋,他們可能會(huì)最終討厭應(yīng)用程序。Flask提供了一種非常簡(jiǎn)單的方法來(lái)向用戶提供閃爍系統(tǒng)的反饋。
消息閃爍使在請(qǐng)求結(jié)束時(shí)記錄一個(gè)消息,并在下一個(gè)(而且只有下一個(gè))請(qǐng)求中訪問(wèn)它變得簡(jiǎn)單。這通常與布局模板組合以展示消息。
使用flash()方法閃爍消息。
您可以使用get_flashed_messages()獲取消息,這個(gè)方法也可以在模板中使用。
查閱Message Flashing獲取更多示例。
Logging
New in version 0.3。
有時(shí)您可能處于這樣的情況下,你處理的數(shù)據(jù)應(yīng)該是正確的,但是實(shí)際上它是錯(cuò)誤的。
例如,您可能有一些客戶端代碼向服務(wù)器發(fā)送HTTP請(qǐng)求,但顯然格式錯(cuò)誤。這可能是由用戶篡改數(shù)據(jù)或客戶端代碼失敗引起的。
大多數(shù)情況下,在這種情況下可以回復(fù)400 Bad Request,但有時(shí)候不會(huì)這樣做,并且代碼必須繼續(xù)工作。
你可能還想記錄發(fā)生了什么事情。這是loggers派上用場(chǎng)的地方。
Flask 0.3版本,預(yù)先配置了一個(gè)logger供您使用。下面是一些示例:
app.logger.debug('A value for debugging')
app.logger.warning('A warning occurred (%d apples)', 42)
app.logger.error('A error occurred')
這個(gè)logger是一個(gè)標(biāo)準(zhǔn)的loggingLogger,
請(qǐng)前往logging documentation獲取更多信息。
更多Application Errors
Hooking in WSGI Middlewares
如果要向應(yīng)用程序添加WSGI中間件,則可以包裝內(nèi)部WSGI應(yīng)用程序。
例如,如果要使用Werkzeug軟件包中的一個(gè)中間件來(lái)處理lighttpd中的錯(cuò)誤,可以這樣做:
from werkzeug.contrib.fixers import LighttpdCGIRootFix
app.wsgi_app = LighttpdCGIRootFix(app.wsgi_app)
使用Flask擴(kuò)展
擴(kuò)展是幫助您完成常見(jiàn)任務(wù)的軟件包。例如,F(xiàn)lask-SQLAlchemy提供SQLAlchemy支持,使其易于與Flask一起使用。
更多Flask擴(kuò)展,請(qǐng)查看Flask Extensions
部署到Web服務(wù)器
準(zhǔn)備好部署你的新Flask應(yīng)用了嗎?參加Deployment Options.