快速入門¶
迫不及待想開始了嗎?本頁面提供 Flask 的良好介紹。請依照 安裝 來設定專案並首先安裝 Flask。
最小的應用程式¶
一個最小的 Flask 應用程式看起來像這樣
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello_world():
return "<p>Hello, World!</p>"
那麼這段程式碼做了什麼呢?
首先,我們匯入了
Flask
類別。這個類別的實例將會是我們的 WSGI 應用程式。接下來,我們建立這個類別的實例。第一個參數是應用程式的模組或套件名稱。
__name__
是一個方便的捷徑,適用於大多數情況。這是必要的,以便 Flask 知道在哪裡尋找資源,例如模板和靜態檔案。然後我們使用
route()
裝飾器來告訴 Flask 哪個 URL 應該觸發我們的函式。該函式返回我們想要在使用者瀏覽器中顯示的訊息。預設內容類型是 HTML,因此字串中的 HTML 將由瀏覽器呈現。
將其儲存為 hello.py
或類似的名稱。請確保不要將您的應用程式命名為 flask.py
,因為這會與 Flask 本身衝突。
要執行應用程式,請使用 flask
命令或 python -m flask
。您需要使用 --app
選項告訴 Flask 您的應用程式在哪裡。
$ flask --app hello run
* Serving Flask app 'hello'
* Running on http://127.0.0.1:5000 (Press CTRL+C to quit)
應用程式探索行為
作為一個捷徑,如果檔案名為 app.py
或 wsgi.py
,您不必使用 --app
。請參閱 命令列介面 以獲取更多詳細資訊。
這會啟動一個非常簡單的內建伺服器,它對於測試來說已經足夠好,但可能不是您想要在生產環境中使用的。有關部署選項,請參閱 部署到生產環境。
現在前往 http://127.0.0.1:5000/,您應該會看到您的 Hello World 問候語。
如果另一個程式已經在使用 Port 5000,您會在伺服器嘗試啟動時看到 OSError: [Errno 98]
或 OSError: [WinError 10013]
。請參閱 位址已被使用 以了解如何處理。
外部可見的伺服器
如果您執行伺服器,您會注意到該伺服器只能從您自己的電腦存取,而不能從網路中的任何其他電腦存取。這是預設行為,因為在偵錯模式下,應用程式的使用者可以在您的電腦上執行任意 Python 程式碼。
如果您停用了偵錯器或信任您網路上的使用者,您可以透過在命令列中新增 --host=0.0.0.0
來使伺服器公開可用
$ flask run --host=0.0.0.0
這會告訴您的作業系統監聽所有公用 IP。
偵錯模式¶
flask run
命令不僅僅可以啟動開發伺服器。透過啟用偵錯模式,如果程式碼變更,伺服器將自動重新載入,並且如果請求期間發生錯誤,將在瀏覽器中顯示互動式偵錯器。

警告
偵錯器允許從瀏覽器執行任意 Python 程式碼。它受到 Pin 碼保護,但仍然代表重大的安全風險。請勿在生產環境中執行開發伺服器或偵錯器。
要啟用偵錯模式,請使用 --debug
選項。
$ flask --app hello run --debug
* Serving Flask app 'hello'
* Debug mode: on
* Running on http://127.0.0.1:5000 (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: nnn-nnn-nnn
另請參閱
HTML 跳脫¶
當返回 HTML(Flask 中的預設回應類型)時,輸出中呈現的任何使用者提供的值都必須經過跳脫,以防止注入攻擊。使用 Jinja 呈現的 HTML 模板(稍後介紹)將自動執行此操作。
此處顯示的 escape()
可以手動使用。為了簡潔起見,它在大多數範例中被省略,但您應該始終注意如何使用不受信任的資料。
from markupsafe import escape
@app.route("/<name>")
def hello(name):
return f"Hello, {escape(name)}!"
如果使用者設法提交名稱 <script>alert("bad")</script>
,跳脫會使其呈現為文字,而不是在使用者的瀏覽器中執行腳本。
路由中的 <name>
從 URL 捕獲一個值並將其傳遞給視圖函式。這些變數規則將在下面解釋。
路由¶
現代 Web 應用程式使用有意義的 URL 來幫助使用者。如果頁面使用使用者可以記住並用於直接訪問頁面的有意義的 URL,則使用者更有可能喜歡該頁面並再次回來。
使用 route()
裝飾器將函式綁定到 URL。
@app.route('/')
def index():
return 'Index Page'
@app.route('/hello')
def hello():
return 'Hello, World'
您可以做更多的事情!您可以使 URL 的某些部分成為動態的,並將多個規則附加到一個函式。
變數規則¶
您可以透過使用 <variable_name>
標記區段,將變數區段新增至 URL。然後您的函式會收到 <variable_name>
作為關鍵字引數。或者,您可以使用轉換器來指定引數的類型,例如 <converter:variable_name>
。
from markupsafe import escape
@app.route('/user/<username>')
def show_user_profile(username):
# show the user profile for that user
return f'User {escape(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 f'Post {post_id}'
@app.route('/path/<path:subpath>')
def show_subpath(subpath):
# show the subpath after /path/
return f'Subpath {escape(subpath)}'
轉換器類型
|
(預設) 接受任何不帶斜線的文字 |
|
接受正整數 |
|
接受正浮點數值 |
|
類似於 |
|
接受 UUID 字串 |
唯一 URL / 重定向行為¶
以下兩個規則在使用尾部斜線方面有所不同。
@app.route('/projects/')
def projects():
return 'The project page'
@app.route('/about')
def about():
return 'The about page'
projects
端點的標準 URL 具有尾部斜線。它類似於檔案系統中的資料夾。如果您存取不帶尾部斜線的 URL (/projects
),Flask 會將您重定向到帶有尾部斜線的標準 URL (/projects/
)。
about
端點的標準 URL 沒有尾部斜線。它類似於檔案的路徑名稱。使用尾部斜線存取 URL (/about/
) 會產生 404 “找不到”錯誤。這有助於保持這些資源的 URL 唯一性,這有助於搜尋引擎避免對同一個頁面建立索引兩次。
URL 建構¶
要建構特定函式的 URL,請使用 url_for()
函式。它接受函式名稱作為其第一個引數,以及任意數量的關鍵字引數,每個關鍵字引數都對應於 URL 規則的變數部分。未知的變數部分會附加到 URL 作為查詢參數。
為什麼您要使用 URL 反向函式 url_for()
而不是將 URL 硬編碼到您的模板中來建構 URL?
反向通常比硬編碼 URL 更具描述性。
您可以一次性變更您的 URL,而無需記住手動變更硬編碼的 URL。
URL 建構會透明地處理特殊字元的跳脫。
產生的路徑始終是絕對路徑,避免了瀏覽器中相對路徑的意外行為。
如果您的應用程式放置在 URL 根目錄之外,例如,在
/myapplication
而不是/
中,url_for()
會為您正確處理。
例如,在這裡我們使用 test_request_context()
方法來試用 url_for()
。test_request_context()
告訴 Flask 表現得好像它正在處理請求,即使我們正在使用 Python Shell。請參閱 上下文區域變數。
from flask import url_for
@app.route('/')
def index():
return 'index'
@app.route('/login')
def login():
return 'login'
@app.route('/user/<username>')
def profile(username):
return f'{username}\'s profile'
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
HTTP 方法¶
Web 應用程式在存取 URL 時使用不同的 HTTP 方法。當您使用 Flask 時,您應該熟悉 HTTP 方法。預設情況下,路由僅回應 GET
請求。您可以使用 route()
裝飾器的 methods
引數來處理不同的 HTTP 方法。
from flask import request
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
return do_the_login()
else:
return show_the_login_form()
上面的範例將路由的所有方法都保留在一個函式中,如果每個部分都使用一些通用資料,這可能會很有用。
您也可以將不同方法的視圖分離到不同的函式中。Flask 為使用 get()
、post()
等裝飾此類路由提供了一個捷徑,適用於每個常見的 HTTP 方法。
@app.get('/login')
def login_get():
return show_the_login_form()
@app.post('/login')
def login_post():
return do_the_login()
如果存在 GET
,Flask 會自動新增對 HEAD
方法的支援,並根據 HTTP RFC 處理 HEAD
請求。同樣,OPTIONS
也會自動為您實作。
靜態檔案¶
動態 Web 應用程式也需要靜態檔案。這通常是 CSS 和 JavaScript 檔案的來源。理想情況下,您的 Web 伺服器已設定為為您提供這些檔案,但在開發期間,Flask 也可以做到這一點。只需在您的套件中或模組旁邊建立一個名為 static
的資料夾,它將在應用程式上的 /static
中可用。
要產生靜態檔案的 URL,請使用特殊的 'static'
端點名稱
url_for('static', filename='style.css')
該檔案必須以 static/style.css
儲存在檔案系統上。
呈現模板¶
從 Python 內部產生 HTML 並不好玩,而且實際上相當麻煩,因為您必須自己執行 HTML 跳脫以保持應用程式安全。因此,Flask 會自動為您設定 Jinja2 模板引擎。
模板可用於產生任何類型的文字檔案。對於 Web 應用程式,您主要會產生 HTML 頁面,但您也可以產生 Markdown、電子郵件的純文字以及任何其他內容。
如需 HTML、CSS 和其他 Web API 的參考,請使用 MDN Web Docs。
要呈現模板,您可以使用 render_template()
方法。您只需提供模板的名稱和您想要傳遞給模板引擎的變數作為關鍵字引數。以下是如何呈現模板的簡單範例
from flask import render_template
@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
return render_template('hello.html', person=name)
Flask 將在 templates
資料夾中尋找模板。因此,如果您的應用程式是一個模組,則此資料夾位於該模組旁邊,如果它是一個套件,則實際上位於您的套件內部
情境 1:模組
/application.py
/templates
/hello.html
情境 2:套件
/application
/__init__.py
/templates
/hello.html
對於模板,您可以使用 Jinja2 模板的全部功能。前往官方 Jinja2 模板文件 以獲取更多資訊。
這是一個範例模板
<!doctype html>
<title>Hello from Flask</title>
{% if person %}
<h1>Hello {{ person }}!</h1>
{% else %}
<h1>Hello, World!</h1>
{% endif %}
在模板內部,您還可以存取 config
、request
、session
和 g
[1] 物件,以及 url_for()
和 get_flashed_messages()
函式。
如果使用繼承,模板尤其有用。如果您想知道它是如何運作的,請參閱 模板繼承。基本上,模板繼承可以使每個頁面上都保留某些元素(例如標頭、導覽和頁腳)。
自動跳脫已啟用,因此如果 person
包含 HTML,它將自動跳脫。如果您可以信任變數,並且您知道它是安全的 HTML(例如,因為它來自將 Wiki 標記轉換為 HTML 的模組),您可以使用 Markup
類別或在模板中使用 |safe
篩選器將其標記為安全。前往 Jinja 2 文件以獲取更多範例。
這是關於 Markup
類別如何運作的基本介紹
>>> from markupsafe import Markup
>>> Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>'
Markup('<strong>Hello <blink>hacker</blink>!</strong>')
>>> Markup.escape('<blink>hacker</blink>')
Markup('<blink>hacker</blink>')
>>> Markup('<em>Marked up</em> » HTML').striptags()
'Marked up » HTML'
變更日誌
在版本 0.5 中變更:不再為所有模板啟用自動跳脫。模板的以下副檔名會觸發自動跳脫:.html
、.htm
、.xml
、.xhtml
。從字串載入的模板將停用自動跳脫。
存取請求資料¶
對於 Web 應用程式來說,回應用戶端傳送到伺服器的資料至關重要。在 Flask 中,此資訊由全域 request
物件提供。如果您有一些 Python 經驗,您可能會想知道該物件如何成為全域物件,以及 Flask 如何管理仍然是執行緒安全的。答案是上下文區域變數
上下文區域變數¶
內幕消息
如果您想了解其運作方式以及如何使用上下文區域變數實作測試,請閱讀本節,否則直接跳過它。
Flask 中的某些物件是全域物件,但不是通常的那種。這些物件實際上是特定上下文中區域物件的代理。真是拗口。但實際上這很容易理解。
想像一下上下文是處理執行緒。請求傳入,Web 伺服器決定產生一個新的執行緒(或其他東西,底層物件能夠處理執行緒以外的並行系統)。當 Flask 啟動其內部請求處理時,它會確定目前執行緒是活動上下文,並將目前的應用程式和 WSGI 環境綁定到該上下文(執行緒)。它以智慧的方式執行此操作,以便一個應用程式可以調用另一個應用程式而不會中斷。
那麼這對您意味著什麼呢?基本上,您可以完全忽略這種情況,除非您正在執行單元測試之類的操作。您會注意到,依賴請求物件的程式碼會突然中斷,因為沒有請求物件。解決方案是自行建立請求物件並將其綁定到上下文。單元測試最簡單的解決方案是使用 test_request_context()
上下文管理器。與 with
語句結合使用,它將綁定測試請求,以便您可以與之互動。這是一個範例
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'
另一種可能性是將整個 WSGI 環境傳遞給 request_context()
方法
with app.request_context(environ):
assert request.method == 'POST'
請求物件¶
請求物件在 API 節中記錄,我們不會在此處詳細介紹(請參閱 Request
)。以下是一些最常見操作的廣泛概述。首先,您必須從 flask
模組匯入它
from flask import request
目前的請求方法可透過使用 method
屬性取得。要存取表單資料(在 POST
或 PUT
請求中傳輸的資料),您可以使用 form
屬性。以下是上面提到的兩個屬性的完整範例
@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)
如果 form
屬性中不存在金鑰會發生什麼情況?在這種情況下,會引發特殊的 KeyError
。您可以像標準 KeyError
一樣捕獲它,但如果您不這樣做,則會顯示 HTTP 400 Bad Request 錯誤頁面。因此,在許多情況下,您不必處理這個問題。
要存取在 URL 中提交的參數 (?key=value
),您可以使用 args
屬性
searchword = request.args.get('key', '')
我們建議使用 get
或透過捕獲 KeyError
來存取 URL 參數,因為使用者可能會變更 URL,並且在這種情況下向他們顯示 400 Bad Request 頁面並不友善。
如需請求物件的方法和屬性的完整清單,請前往 Request
文件。
檔案上傳¶
您可以使用 Flask 輕鬆處理上傳的檔案。只需確保不要忘記在您的 HTML 表單上設定 enctype="multipart/form-data"
屬性,否則瀏覽器根本不會傳輸您的檔案。
上傳的檔案儲存在記憶體中或檔案系統上的暫存位置。您可以透過查看請求物件上的 files
屬性來存取這些檔案。每個上傳的檔案都儲存在該字典中。它的行為就像標準 Python file
物件一樣,但它也具有 save()
方法,可讓您將該檔案儲存在伺服器的檔案系統上。以下是一個簡單的範例,展示了它是如何運作的
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')
...
如果您想知道檔案在上傳到您的應用程式之前在用戶端上的名稱,您可以存取 filename
屬性。但是請記住,此值可能會被偽造,因此永遠不要信任該值。如果您想使用用戶端的檔案名稱將檔案儲存在伺服器上,請將其傳遞給 Werkzeug 為您提供的 secure_filename()
函式
from werkzeug.utils import secure_filename
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
file = request.files['the_file']
file.save(f"/var/www/uploads/{secure_filename(file.filename)}")
...
如需更好的範例,請參閱 上傳檔案。
重定向和錯誤¶
要將使用者重定向到另一個端點,請使用 redirect()
函式;要提前中止請求並顯示錯誤碼,請使用 abort()
函式
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()
這是一個相當無意義的範例,因為使用者將從索引重定向到他們無法存取的頁面(401 表示拒絕存取),但它展示了它是如何運作的。
預設情況下,每個錯誤碼都會顯示黑白錯誤頁面。如果您想要自訂錯誤頁面,可以使用 errorhandler()
裝飾器
from flask import render_template
@app.errorhandler(404)
def page_not_found(error):
return render_template('page_not_found.html'), 404
請注意 render_template()
呼叫之後的 404
。這告訴 Flask 該頁面的狀態碼應為 404,表示找不到。預設情況下,假設為 200,表示:一切順利。
有關更多詳細資訊,請參閱 處理應用程式錯誤。
關於回應¶
檢視函式的傳回值會自動轉換為回應物件。如果傳回值是字串,它會轉換為回應物件,並將該字串作為回應主體、200 OK
狀態碼和 text/html mimetype。如果傳回值是 dict 或 list,則會呼叫 jsonify()
以產生回應。Flask 將傳回值轉換為回應物件的邏輯如下:
如果傳回正確類型的回應物件,則會直接從檢視函式傳回。
如果傳回值是字串,則會使用該資料和預設參數建立回應物件。
如果傳回值是迭代器或產生器,傳回字串或位元組,則會將其視為串流回應。
如果傳回值是 dict 或 list,則會使用
jsonify()
建立回應物件。如果傳回的是元組,則元組中的項目可以提供額外資訊。此類元組的格式必須為
(response, status)
、(response, headers)
或(response, status, headers)
。status
值將覆寫狀態碼,而headers
可以是額外標頭值的列表或字典。如果以上方法都不適用,Flask 將假設傳回值是有效的 WSGI 應用程式,並將其轉換為回應物件。
如果您想在檢視函式內取得產生的回應物件,可以使用 make_response()
函式。
假設您有一個像這樣的檢視函式
from flask import render_template
@app.errorhandler(404)
def not_found(error):
return render_template('error.html'), 404
您只需要用 make_response()
包裝傳回表達式,並取得回應物件以進行修改,然後傳回它
from flask import make_response
@app.errorhandler(404)
def not_found(error):
resp = make_response(render_template('error.html'), 404)
resp.headers['X-Something'] = 'A value'
return resp
具有 JSON 的 API¶
撰寫 API 時,常見的回應格式是 JSON。使用 Flask 可以輕鬆開始撰寫此類 API。如果您從檢視函式傳回 dict
或 list
,它將會轉換為 JSON 回應。
@app.route("/me")
def me_api():
user = get_current_user()
return {
"username": user.username,
"theme": user.theme,
"image": url_for("user_image", filename=user.image),
}
@app.route("/users")
def users_api():
users = get_all_users()
return [user.to_json() for user in users]
這是將資料傳遞給 jsonify()
函式的快捷方式,該函式將序列化任何支援的 JSON 資料類型。這表示 dict 或 list 中的所有資料都必須是 JSON 可序列化的。
對於複雜類型(例如資料庫模型),您會希望使用序列化函式庫,先將資料轉換為有效的 JSON 類型。有許多序列化函式庫和 Flask API 擴充套件由社群維護,支援更複雜的應用程式。
Sessions(會話)¶
除了請求物件之外,還有第二個稱為 session
的物件,可讓您儲存特定於使用者的資訊,從一個請求到下一個請求。這是透過 cookies 為您實作的,並以密碼學方式簽署 cookies。這表示使用者可以查看您的 cookie 內容,但無法修改它,除非他們知道用於簽署的密鑰。
為了使用 sessions,您必須設定一個密鑰。以下是 sessions 的運作方式
from flask import session
# Set the secret key to some random bytes. Keep this really secret!
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'
@app.route('/')
def index():
if 'username' in session:
return f'Logged in as {session["username"]}'
return 'You are not logged in'
@app.route('/login', methods=['GET', 'POST'])
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'))
如何產生良好的密鑰
密鑰應盡可能隨機。您的作業系統有方法可以根據密碼學隨機產生器產生相當隨機的資料。使用以下命令快速產生 Flask.secret_key
(或 SECRET_KEY
)的值
$ python -c 'import secrets; print(secrets.token_hex())'
'192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'
關於基於 cookie 的 sessions 的注意事項:Flask 將採用您放入 session 物件中的值,並將它們序列化為 cookie。如果您發現某些值無法跨請求持續存在,cookies 確實已啟用,並且您沒有收到明確的錯誤訊息,請檢查頁面回應中 cookie 的大小,與網路瀏覽器支援的大小相比。
除了預設的用戶端 sessions 之外,如果您想在伺服器端處理 sessions,還有幾個 Flask 擴充套件支援此功能。
訊息快閃¶
良好的應用程式和使用者介面都與回饋有關。如果使用者沒有獲得足夠的回饋,他們可能會討厭該應用程式。Flask 提供了一種非常簡單的方式,透過快閃系統向使用者提供回饋。快閃系統基本上可以記錄請求結束時的訊息,並在下一個(且僅限下一個)請求中存取它。這通常與版面配置模板結合使用,以顯示訊息。
要快閃訊息,請使用 flash()
方法,要取得訊息,您可以使用 get_flashed_messages()
,它也適用於模板。有關完整範例,請參閱 訊息快閃。
記錄¶
變更日誌
在 0.3 版本中新增。
有時您可能會遇到處理應該正確但實際上不正確的資料的情況。例如,您可能有某些用戶端程式碼向伺服器發送 HTTP 請求,但顯然格式錯誤。這可能是由於使用者篡改資料或用戶端程式碼故障所致。在大多數情況下,在這種情況下回覆 400 Bad Request
是可以的,但有時這是不夠的,程式碼必須繼續運作。
您可能仍然想記錄下發生的可疑事件。這就是記錄器的用武之地。從 Flask 0.3 開始,預先為您設定了一個記錄器以供使用。
以下是一些範例記錄呼叫
app.logger.debug('A value for debugging')
app.logger.warning('A warning occurred (%d apples)', 42)
app.logger.error('An error occurred')
附加的 logger
是一個標準的記錄 Logger
,因此請前往官方 logging
文件以獲取更多資訊。
請參閱 處理應用程式錯誤。
掛鉤 WSGI 中介軟體¶
若要將 WSGI 中介軟體新增至您的 Flask 應用程式,請包裝應用程式的 wsgi_app
屬性。例如,若要在 Nginx 後方執行時套用 Werkzeug 的 ProxyFix
中介軟體
from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app)
包裝 app.wsgi_app
而不是 app
表示 app
仍然指向您的 Flask 應用程式,而不是中介軟體,因此您可以繼續直接使用和設定 app
。
使用 Flask 擴充套件¶
擴充套件是協助您完成常見任務的套件。例如,Flask-SQLAlchemy 提供了 SQLAlchemy 支援,使其可以簡單方便地與 Flask 一起使用。
有關 Flask 擴充套件的更多資訊,請參閱 擴充套件。
部署到 Web 伺服器¶
準備好部署您的新 Flask 應用程式了嗎?請參閱 部署到生產環境。