Flask---框架快速入門

迫不及待要開始了嗎?本頁提供了一個很好的 Flask 介紹,並假定你已經安裝好了 Flask。如果沒有,請跳轉到 安裝 章節。

一個最小的應用

一個最小的 Flask 應用看起來會是這樣:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello World!'

if __name__ == '__main__':
    app.run()

把它保存爲 hello.py (或是類似的),然後用 Python 解釋器來運行。 確保你的應用文件名不是 flask.py ,因爲這將與 Flask 本身衝突。

$ python hello.py
 * Running on http://127.0.0.1:5000/

現在訪問 http://127.0.0.1:5000/ ,你會看見 Hello World 問候。

那麼,這段代碼做了什麼?

  1. 首先,我們導入了 Flask 類。這個類的實例將會是我們的 WSGI 應用程序。
  2. 接下來,我們創建一個該類的實例,第一個參數是應用模塊或者包的名稱。 如果你使用單一的模塊(如本例),你應該使用 __name__ ,因爲模塊的名稱將會因其作爲單獨應用啓動還是作爲模塊導入而有不同( 也即是 '__main__' 或實際的導入名)。這是必須的,這樣 Flask 才知道到哪去找模板、靜態文件等等。詳情見 Flask的文檔。
  3. 然後,我們使用 route() 裝飾器告訴 Flask 什麼樣的URL 能觸發我們的函數。
  4. 這個函數的名字也在生成 URL 時被特定的函數採用,這個函數返回我們想要顯示在用戶瀏覽器中的信息。
  5. 最後我們用 run() 函數來讓應用運行在本地服務器上。 其中 if __name__ =='__main__': 確保服務器只會在該腳本被 Python 解釋器直接執行的時候纔會運行,而不是作爲模塊導入的時候。

欲關閉服務器,按 Ctrl+C。

外部可訪問的服務器

如果你運行了這個服務器,你會發現它只能從你自己的計算機上訪問,網絡中其它任何的地方都不能訪問。在調試模式下,用戶可以在你的計算機上執行任意 Python 代碼。因此,這個行爲是默認的。

如果你禁用了 debug 或信任你所在網絡的用戶,你可以簡單修改調用 run() 的方法使你的服務器公開可用,如下:

app.run(host='0.0.0.0')

這會讓操作系統監聽所有公網 IP。

調試模式

雖然 run() 方法適用於啓動本地的開發服務器,但是你每次修改代碼後都要手動重啓它。這樣並不夠優雅,而且 Flask 可以做到更好。如果你啓用了調試支持,服務器會在代碼修改後自動重新載入,並在發生錯誤時提供一個相當有用的調試器。

有兩種途徑來啓用調試模式。一種是直接在應用對象上設置:

app.debug = True
app.run()

另一種是作爲 run 方法的一個參數傳入:

app.run(debug=True)

兩種方法的效果完全相同。

注意

儘管交互式調試器在允許 fork 的環境中無法正常使用(也即在生產服務器上正常使用幾乎是不可能的),但它依然允許執行任意代碼。這使它成爲一個巨大的安全隱患,因此它 絕對不能用於生產環境 。

運行中的調試器截圖:

screenshot of debugger in action

想用其它的調試器? 參見 調試器操作 。

路由

現代 Web 應用的 URL 十分優雅,易於人們辨識記憶,這一點對於那些面向使用低速網絡連接移動設備訪問的應用特別有用。如果可以不訪問索引頁,而是直接訪問想要的那個頁面,他們多半會笑逐顏開而再度光顧。

如上所見, route() 裝飾器把一個函數綁定到對應的 URL 上。

這裏是一些基本的例子:

@app.route('/')
def index():
    return 'Index Page'

@app.route('/hello')
def hello():
    return 'Hello World'

但是,不僅如此!你可以構造含有動態部分的 URL,也可以在一個函數上附着多個規則。

變量規則

要給 URL 添加變量部分,你可以把這些特殊的字段標記爲 <variable_name> , 這個部分將會作爲命名參數傳遞到你的函數。規則可以用 <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

轉換器有下面幾種:

int接受整數
float同 int ,但是接受浮點數
path和默認的相似,但也接受斜線

唯一 URL / 重定向行爲

Flask 的 URL 規則基於 Werkzeug 的路由模塊。這個模塊背後的思想是基於 Apache 以及更早的 HTTP 服務器主張的先例,保證優雅且唯一的 URL。

以這兩個規則爲例:

@app.route('/projects/')
def projects():
    return 'The project page'

@app.route('/about')
def about():
    return 'The about page'

雖然它們看起來着實相似,但它們結尾斜線的使用在 URL 定義 中不同。 第一種情況中,指向 projects 的規範 URL 尾端有一個斜線。這種感覺很像在文件系統中的文件夾。訪問一個結尾不帶斜線的 URL 會被 Flask 重定向到帶斜線的規範 URL 去。

然而,第二種情況的 URL 結尾不帶斜線,類似 UNIX-like 系統下的文件的路徑名。訪問結尾帶斜線的 URL 會產生一個 404 “Not Found” 錯誤。

這個行爲使得在遺忘尾斜線時,允許關聯的 URL 接任工作,與 Apache 和其它的服務器的行爲並無二異。此外,也保證了 URL 的唯一,有助於避免搜索引擎索引同一個頁面兩次。

構造 URL

如果 Flask 能匹配 URL,那麼 Flask 可以生成它們嗎?當然可以。你可以用 url_for()來給指定的函數構造 URL。它接受函數名作爲第一個參數,也接受對應 URL 規則的變量部分的命名參數。未知變量部分會添加到 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() 方法,下面會解釋。即使我們正在通過 Python 的 shell 進行交互,它依然會告訴 Flask 要表現爲正在處理一個請求。請看下面的解釋。 環境局部變量 )

爲什麼你要構建 URL 而非在模板中硬編碼?這裏有三個絕妙的理由:

  1. 反向構建通常比硬編碼的描述性更好。更重要的是,它允許你一次性修改 URL, 而不是到處邊找邊改。
  2. URL 構建會轉義特殊字符和 Unicode 數據,免去你很多麻煩。
  3. 如果你的應用不位於 URL 的根路徑(比如,在 /myapplication 下,而不是 / ),url_for() 會妥善處理這個問題。

HTTP 方法

HTTP (與 Web 應用會話的協議)有許多不同的訪問 URL 方法。默認情況下,路由只回應 GET 請求,但是通過 route() 裝飾器傳遞 methods 參數可以改變這個行爲。這裏有一些例子:

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        do_the_login()
    else:
        show_the_login_form()

如果存在 GET ,那麼也會替你自動地添加 HEAD,無需干預。它會確保遵照 HTTP RFC(描述 HTTP 協議的文檔)處理 HEAD 請求,所以你可以完全忽略這部分的 HTTP 規範。同樣,自從 Flask 0.6 起, 也實現了 OPTIONS 的自動處理。

你不知道一個 HTTP 方法是什麼?不必擔心,這裏會簡要介紹 HTTP 方法和它們爲什麼重要:

HTTP 方法(也經常被叫做“謂詞”)告知服務器,客戶端想對請求的頁面  些什麼。下面的都是非常常見的方法:

GET
瀏覽器告知服務器:只 獲取 頁面上的信息併發給我。這是最常用的方法。
HEAD
瀏覽器告訴服務器:欲獲取信息,但是隻關心 消息頭 。應用應像處理 GET 請求一樣來處理它,但是不分發實際內容。在 Flask 中你完全無需 人工 干預,底層的 Werkzeug 庫已經替你打點好了。
POST
瀏覽器告訴服務器:想在 URL 上 發佈 新信息。並且,服務器必須確保 數據已存儲且僅存儲一次。這是 HTML 表單通常發送數據到服務器的方法。
PUT
類似 POST 但是服務器可能觸發了存儲過程多次,多次覆蓋掉舊值。你可 能會問這有什麼用,當然這是有原因的。考慮到傳輸中連接可能會丟失,在 這種 情況下瀏覽器和服務器之間的系統可能安全地第二次接收請求,而 不破壞其它東西。因爲 POST它只觸發一次,所以用 POST 是不可能的。
DELETE
刪除給定位置的信息。
OPTIONS
給客戶端提供一個敏捷的途徑來弄清這個 URL 支持哪些 HTTP 方法。 從 Flask 0.6 開始,實現了自動處理。

有趣的是,在 HTML4 和 XHTML1 中,表單只能以 GET 和 POST 方法提交到服務器。但是 JavaScript 和未來的 HTML 標準允許你使用其它所有的方法。此外,HTTP 最近變得相當流行,瀏覽器不再是唯一的 HTTP 客戶端。比如,許多版本控制系統就在使用 HTTP。

靜態文件

動態 web 應用也會需要靜態文件,通常是 CSS 和 JavaScript 文件。理想狀況下, 你已經配置好 Web 服務器來提供靜態文件,但是在開發中,Flask 也可以做到。 只要在你的包中或是模塊的所在目錄中創建一個名爲 static 的文件夾,在應用中使用 /static 即可訪問。

給靜態文件生成 URL ,使用特殊的 'static' 端點名:

url_for('static', filename='style.css')

這個文件應該存儲在文件系統上的 static/style.css 。

模板渲染

用 Python 生成 HTML 十分無趣,而且相當繁瑣,因爲你必須手動對 HTML 做轉義來保證應用的安全。爲此,Flask 配備了 Jinja2 模板引擎。

你可以使用 render_template() 方法來渲染模板。你需要做的一切就是將模板名和你想作爲關鍵字的參數傳入模板的變量。這裏有一個展示如何渲染模板的簡例:

from flask import render_template

@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
    return render_template('hello.html', name=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 name %}
  <h1>Hello {{ name }}!</h1>
{% else %}
  <h1>Hello World!</h1>
{% endif %}

在模板裏,你也可以訪問 request 、 session 和 g [1] 對象, 以及get_flashed_messages() 函數。

模板繼承讓模板用起來相當順手。如欲瞭解繼承的工作機理,請跳轉到 模板繼承 模式的文檔。最起碼,模板繼承能使特定元素 (比如頁眉、導航欄和頁腳)可以出現在所有的頁面。

自動轉義功能默認是開啓的,所以如果 name 包含 HTML ,它將會被自動轉義。如果你能信任一個變量,並且你知道它是安全的(例如一個模塊把 Wiki 標記轉換爲 HTML),你可以用 Markup 類或 |safe 過濾器在模板中把它標記爲安全的。在 Jinja 2 文檔中,你會看到更多的例子。

這裏是一個 Markup 類如何使用的簡單介紹:

>>> from flask import Markup
>>> Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>'
Markup(u'<strong>Hello &lt;blink&gt;hacker&lt;/blink&gt;!</strong>')
>>> Markup.escape('<blink>hacker</blink>')
Markup(u'&lt;blink&gt;hacker&lt;/blink&gt;')
>>> Markup('<em>Marked up</em> &raquo; HTML').striptags()
u'Marked up \xbb HTML'

在 0.5 版更改: 自動轉義不再在所有模板中啓用。下列擴展名的模板會觸發自動轉義:.html 、 .htm 、.xml 、 .xhtml 。從字符串加載的模板會禁用自動轉義。

[1]不確定 g 對象是什麼?它允許你按需存儲信息, 查看( g )對象的文檔和 在 Flask 中使用 SQLite 3 的文檔以獲取更多信息。

訪問請求數據

對於 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() 方法:

from flask import request

with app.request_context(environ):
    assert request.method == 'POST'

請求對象

API 章節對請求對象作了詳盡闡述(參見 request ),因此這裏不會贅述。此處寬泛介紹一些最常用的操作。首先從 flask 模塊裏導入它:

from flask import request

當前請求的 HTTP 方法可通過 method 屬性來訪問。通過:attr:~flask.request.form 屬性來訪問表單數據( POST 或 PUT 請求提交的數據)。這裏有一個用到上面提到的那兩個屬性的完整實例:

@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 錯誤頁面。所以,多數情況下你並不需要干預這個行爲。

你可以通過 args 屬性來訪問 URL 中提交的參數 ( ?key=value ):

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

我們推薦用 get 來訪問 URL 參數或捕獲 KeyError ,因爲用戶可能會修改 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 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))
    ...

一些更好的例子,見 上傳文件 模式。

Cookies

你可以通過 cookies 屬性來訪問 Cookies,用響應對象的 set_cookie 方法來設置 Cookies。請求對象的 cookies 屬性是一個內容爲客戶端提交的所有 Cookies 的字典。如果你想使用會話,請不要直接使用 Cookies,請參考 會話 一節。在 Flask 中,已經注意處理了一些 Cookies 安全細節。

讀取 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.

存儲 cookies:

from flask import make_response

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

可注意到的是,Cookies 是設置在響應對象上的。由於通常視圖函數只是返回字符串,之後 Flask 將字符串轉換爲響應對象。如果你要顯式地轉換,你可以使用make_response() 函數然後再進行修改。

有時候你想設置 Cookie,但響應對象不能醋在。這可以利用 延遲請求回調 模式實現。

爲此,也可以閱讀 關於響應 。

重定向和錯誤

你可以用 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``的  MIME 類型是 ``text/html 的響應對象。Flask 把返回值轉換爲響應對象的邏輯是這樣:

  1. 如果返回的是一個合法的響應對象,它會從視圖直接返回。
  2. 如果返回的是一個字符串,響應對象會用字符串數據和默認參數創建。
  3. 如果返回的是一個元組,且元組中的元素可以提供額外的信息。這樣的元組必須是(response, status, headers) 的形式,且至少包含一個元素。 status 值會覆蓋狀態代碼, headers 可以是一個列表或字典,作爲額外的消息標頭值。
  4. 如果上述條件均不滿足, Flask 會假設返回值是一個合法的 WSGI 應用程序,並轉換爲一個請求對象。

如果你想在視圖裏操縱上述步驟結果的響應對象,可以使用 make_response() 函數。

譬如你有這樣一個視圖:

@app.errorhandler(404)
def not_found(error):
    return render_template('error.html'), 404

你只需要把返回值表達式傳遞給 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

會話

除請求對象之外,還有一個 session 對象。它允許你在不同請求間存儲特定用戶的信息。它是在 Cookies 的基礎上實現的,並且對 Cookies 進行密鑰簽名。這意味着用戶可以查看你 Cookie 的內容,但卻不能修改它,除非用戶知道簽名的密鑰。

要使用會話,你需要設置一個密鑰。這裏介紹會話如何工作:

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=['GET', 'POST'])
def login():
    if request.method == 'POST':
        session['username'] = request.form['username']
        return redirect(url_for('index'))
    return '''
        <form action="" 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() 可以在你模板引擎外做轉義(如同本例)。

如何生成強壯的密鑰

隨機的問題在於很難判斷什麼是真隨機。一個密鑰應該足夠隨機。你的操作系統可以基於一個密鑰隨機生成器來生成漂亮的隨機值,這個值可以用來做密鑰:

>>> import os
>>> os.urandom(24)
'\xfd{H\xe5<\x95\xf9\xe3\x96.5\xd1\x01O<!\xd5\xa2\xa0\x9fR"\xa1\xa8'

把這個值複製粘貼進你的代碼中,你就有了密鑰。

使用基於 cookie 的會話需注意: Flask 會將你放進會話對象的值序列化至 Cookies。如果你發現某些值在請求之間並沒有持久存在,然而確實已經啓用了 Cookies,但也沒有得到明確的錯誤信息。這時,請檢查你的頁面響應中的 Cookies 的大小,並與 Web 瀏覽器所支持的大小對比。

消息閃現

反饋,是良好的應用和用戶界面的重要構成。如果用戶得不到足夠的反饋,他們很可能開始厭惡這個應用。 Flask 提供了消息閃現系統,可以簡單地給用戶反饋。 消息閃現系統通常會在請求結束時記錄信息,並在下一個(且僅在下一個)請求中訪問記錄的信息。展現這些消息通常結合要模板佈局。

使用 flash() 方法可以閃現一條消息。要操作消息本身,請使用get_flashed_messages() 函數,並且在模板中也可以使用。完整的例子見 消息閃現 部分。

日誌記錄

0.3 新版功能.

有時候你會處於這樣一種境地,你處理的數據本應該是正確的,但實際上不是。 比如,你會有一些向服務器發送請求的客戶端代碼,但請求顯然是畸形的。這可能是用戶篡改了數據,或是客戶端代碼的粗製濫造。大多數情況下,正常地返回 400 Bad Request 就可以了,但是有時候不能這麼做,並且要讓代碼繼續運行。

你可能依然想要記錄下,是什麼不對勁。這時日誌記錄就派上了用場。從 Flask 0.3 開始,Flask 就已經預置了日誌系統。

這裏有一些調用日誌記錄的例子:

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 中間件,你可以封裝內部 WSGI 應用。例如若是你想用 Werkzeug 包中的某個中間件來應付 lighttpd 中的 bugs ,可以這樣做:

from werkzeug.contrib.fixers import LighttpdCGIRootFix
app.wsgi_app = LighttpdCGIRootFix(app.wsgi_app)

部署到 Web 服務器

準備好部署你的 Flask 應用了?你可以立即部署到託管平臺來圓滿完成快速入門,以下廠商均向小項目提供免費的方案:

託管 Flask 應用的其它選擇:

如果你有自己的主機,並且準備自己託管,參見 部署選擇 章節。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章