Flask 訪問(wèn)請(qǐng)求數(shù)據(jù)

2021-08-10 11:44 更新

對(duì)于 Web 應(yīng)用,與客戶端發(fā)送給服務(wù)器的數(shù)據(jù)交互至關(guān)重要。在 Flask 中 由全局的 request 對(duì)象來(lái)提供這些信息。如果你有一定的 Python 經(jīng)驗(yàn),你會(huì)好奇,為什么這個(gè)對(duì)象是全局的,為什么 Flask 還能保證 線程安全。答案是環(huán)境作用域:

環(huán)境局部變量

內(nèi)幕

如果你想理解其工作機(jī)制及如何利用環(huán)境局部變量實(shí)現(xiàn)自動(dòng)化測(cè)試,請(qǐng)閱 讀此節(jié),否則可跳過(guò)。

Flask 中的某些對(duì)象是全局對(duì)象,但卻不是通常的那種。這些對(duì)象實(shí)際上是特定 環(huán)境的局部對(duì)象的代理。雖然很拗口,但實(shí)際上很容易理解。

想象一下處理線程的環(huán)境。一個(gè)請(qǐng)求傳入,Web 服務(wù)器決定生成一個(gè)新線程( 或者別的什么東西,只要這個(gè)底層的對(duì)象可以勝任并發(fā)系統(tǒng),而不僅僅是線程)。 當(dāng) Flask 開(kāi)始它內(nèi)部的請(qǐng)求處理時(shí),它認(rèn)定當(dāng)前線程是活動(dòng)的環(huán)境,并綁定當(dāng) 前的應(yīng)用和 WSGI 環(huán)境到那個(gè)環(huán)境上(線程)。它的實(shí)現(xiàn)很巧妙,能保證一個(gè)應(yīng) 用調(diào)用另一個(gè)應(yīng)用時(shí)不會(huì)出現(xiàn)問(wèn)題。

所以,這對(duì)你來(lái)說(shuō)意味著什么?除非你要做類(lèi)似單元測(cè)試的東西,否則你基本上 可以完全無(wú)視它。你會(huì)發(fā)現(xiàn)依賴(lài)于一段請(qǐng)求對(duì)象的代碼,因沒(méi)有請(qǐng)求對(duì)象無(wú)法正 常運(yùn)行。解決方案是,自行創(chuàng)建一個(gè)請(qǐng)求對(duì)象并且把它綁定到環(huán)境中。單元測(cè)試 的最簡(jiǎn)單的解決方案是:用 test_request_context() 環(huán) 境管理器。結(jié)合 with 聲明,綁定一個(gè)測(cè)試請(qǐng)求,這樣你才能與之交互。下面 是一個(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ì)象

API 章節(jié)對(duì)請(qǐng)求對(duì)象作了詳盡闡述(參見(jiàn) request ),因此這 里不會(huì)贅述。此處寬泛介紹一些最常用的操作。首先從 flask 模塊里導(dǎo)入它:

from flask import request

當(dāng)前請(qǐng)求的 HTTP 方法可通過(guò) method 屬性來(lái)訪問(wèn)。通 過(guò):attr:~flask.request.form 屬性來(lái)訪問(wèn)表單數(shù)據(jù)( POSTPUT 請(qǐng)求 提交的數(shù)據(jù))。這里有一個(gè)用到上面提到的那兩個(gè)屬性的完整實(shí)例:

@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 method
    # was GET or the credentials were invalid
    return render_template('login.html', error=error)

當(dāng)訪問(wèn) form 屬性中的不存在的鍵會(huì)發(fā)生什么?會(huì)拋出一個(gè)特殊的 KeyError 異常。你可以像捕獲標(biāo)準(zhǔn)的 KeyError 一樣來(lái)捕獲它。 如果你不這么做,它會(huì)顯示一個(gè) HTTP 400 Bad Request 錯(cuò)誤頁(yè)面。所以,多數(shù) 情況下你并不需要干預(yù)這個(gè)行為。

你可以通過(guò) args 屬性來(lái)訪問(wèn) URL 中提交的參數(shù) ( ?key=value ):

searchword = request.args.get('q', '')

我們推薦用 get 來(lái)訪問(wèn) URL 參數(shù)或捕獲 KeyError ,因?yàn)橛脩艨赡軙?huì)修 改 URL,向他們展現(xiàn)一個(gè) 400 bad request 頁(yè)面會(huì)影響用戶體驗(yàn)。

欲獲取請(qǐng)求對(duì)象的完整方法和屬性清單,請(qǐng)參閱 request 的 文檔。

文件上傳

用 Flask 處理文件上傳很簡(jiǎn)單。只要確保你沒(méi)忘記在 HTML 表單中設(shè)置 enctype="multipart/form-data" 屬性,不然你的瀏覽器根本不會(huì)發(fā)送文件。

已上傳的文件存儲(chǔ)在內(nèi)存或是文件系統(tǒng)中一個(gè)臨時(shí)的位置。你可以通過(guò)請(qǐng)求對(duì)象 的 files 屬性訪問(wèn)它們。每個(gè)上傳的文件都會(huì)存儲(chǔ)在 這個(gè)字典里。它表現(xiàn)近乎為一個(gè)標(biāo)準(zhǔn)的 Python file 對(duì)象,但它還有 一個(gè) save() 方法,這個(gè)方法 允許你把文件保存到服務(wù)器的文件系統(tǒng)上。這里是一個(gè)用它保存文件的例子:

from flask import request

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        f = request.files['the_file']
        f.save('/var/www/uploads/uploaded_file.txt')
    ...

如果你想知道上傳前文件在客戶端的文件名是什么,你可以訪問(wèn) filename 屬性。但請(qǐng)記住, 永遠(yuǎn)不要信任這個(gè)值,這個(gè)值是可以偽造的。如果你要把文件按客戶端提供的 文件名存儲(chǔ)在服務(wù)器上,那么請(qǐng)把它傳遞給 Werkzeug 提供的 secure_filename() 函數(shù):

from flask import request
from werkzeug import secure_filename

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        f = request.files['the_file']
        f.save('/var/www/uploads/' + secure_filename(f.filename))
    ...

一些更好的例子,見(jiàn) 上傳文件 模式。

Cookies

你可以通過(guò) cookies 屬性來(lái)訪問(wèn) Cookies,用 響應(yīng)對(duì)象的 set_cookie 方法來(lái)設(shè)置 Cookies。請(qǐng) 求對(duì)象的 cookies 屬性是一個(gè)內(nèi)容為客戶端提交的 所有 Cookies 的字典。如果你想使用會(huì)話,請(qǐng)不要直接使用 Cookies,請(qǐng)參 考 會(huì)話 一節(jié)。在 Flask 中,已經(jīng)注意處理了一些 Cookies 安全 細(xì)節(jié)。

讀取 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 cookie is missing.

存儲(chǔ) cookies:

from flask import make_response

@app.route('/')
def index():
    resp = make_response(render_template(...))
    resp.set_cookie('username', 'the username')
    return resp

可注意到的是,Cookies 是設(shè)置在響應(yīng)對(duì)象上的。由于通常視圖函數(shù)只是返 回字符串,之后 Flask 將字符串轉(zhuǎn)換為響應(yīng)對(duì)象。如果你要顯式地轉(zhuǎn)換,你 可以使用 make_response() 函數(shù)然后再進(jìn)行修改。

有時(shí)候你想設(shè)置 Cookie,但響應(yīng)對(duì)象不能醋在。這可以利用 延遲請(qǐng)求回調(diào) 模式實(shí)現(xiàn)。

為此,也可以閱讀 關(guān)于響應(yīng) 。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)