Flask 成長之路(二)---- Flask的一個簡單示例

上節我們已經安裝好了 Flask ,接下來我們就利用 Flask 寫一個最簡單的示例。

from flask import Flask
app = Flask(__name__)

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

這個程序做了哪些事情呢:

  1. 首先導入了 Flask 類,這個類的實例將會成爲我們的 WSGI 應用。
  2. 接下來我們創建了這個類的實例。傳入的第一個參數是應用程序的模塊名或者包名。__name__ 的值會因爲啓動的是應用程序(__main__)還是模塊(模塊名)會有所不同。Flask 需要這些信息來知道去哪裏找到模板,靜態文件等。
  3. 然後使用 Route() 裝飾器來告訴 Flask 什麼 URL 用來引發我們的函數。
  4. 函數名同樣用來爲特定的函數產生 URLs,函數用來返回我們想要在瀏覽器中展示的信息。

將上面的文件保存爲 hello.py 或者其他的名字,除了 flask.py 因爲這會和 Flask 自身的 flask.py 文件衝突。

運行這個應用,可以使用 flask 命令或者 python 的 -m 轉換到 flask,在此之前,需要通過設置 FLASK_APP 將你的應用程序告訴它將要運行的終端。在 windows 中

set FLASK_APP = hello.py

接下來就可以使用 flask 命令或者 python 來啓動應用

默認處於debugger模式,此模式能夠讓用戶在電腦上運行任意的 python 代碼,但是服務只能在本機上運行。如果要關閉 debugger 模式並讓服務公開,只需要添加 flask run --host=0.0.0.0,他會讓操作系統監聽所有的公共 IP。

如果想要 debug 支持在代碼改變的時候服務器自動重載而不是每次都手動重啓服務,可以在運行服務之前設置 FLASK_ENV 爲development,在 windows 中

set FLASK_ENV = development
flask run

她做了幾件事:

  1. 激活 debugger
  2. 激活自動重載器
  3. 在 flask 應用中使能 debug 模式

也可以通過設置 FLASK_DEBUG = 1 來控制 debug 模式。

使用 route() 裝飾器把一個函數綁定到 URL 上

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

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

還可以使用 <variable_name> 將變量部分加到 URL 中,函數可以接受這個變量作爲參數。可以使用轉換器來確定參數的類型,就像這樣 <converter:variable_name>

@app.route('/user/<username>')
def show_user_profile(username):
    return 'User %s' % username

@app.route('/post/<int:post_id>')
def show_post(post_id):
    return 'Post %d' % post_id

@app.route('/path/<path:subpath>')
def show_subpath(subpath):
    return 'Subpath %s' % subpath

轉換器一共有以下幾種類型

需要注意的是,使用 path 這個轉換器的時候,在變量名後面不加斜線,接收到的是不加斜線的參數;加斜線的時候,接收到的是加斜線的參數。但是對於不是 path 的轉換器,如果路徑中不是以斜線 '/' 結尾的話,而在搜索 URL 的時候加了斜線,就出現 400 Not Found 錯誤。這也是 URL 的 unique 特性,避免重複搜索。

看下面這2種寫法。一種是在末尾加了斜線,一種是沒有在末尾加上斜線。

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

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

規範的 URL 寫法後面是有斜線的。如果在搜索 URL 中沒有加斜線,第一種寫法會讓 flask 重定向到標準的 URL,也就是會自動在搜索 URL 的時候後面加上斜線,從而在搜索的時候 URL 後面是否添加斜線都不會報錯。但是第二種寫法如果在搜索 about 頁面的時候 URL 加了斜線,就會報 404 Not Found 錯誤,這也是 URL 的唯一性所決定的。

我們還可以用 url_for()函數來爲某個函數建立 URL,他接受函數名作爲它的第一個參數,並且可以接受任意數量的關鍵字參數,其中的每一個對應到 URL 規則的變量部分,未知的變量部分將會作爲查詢參數加入到 URL 。

使用 url_for()的理由有以下幾個:

  1. 產生的路徑是絕對的,能夠避免相對路徑的異常行爲。
  2. 能夠改變 URLs 而不用記住並手動改變 URLs

我們使用 test_request_context() 函數來試驗 url_for() ,它讓 Flask 在我們使用 Python shell 時像是處理一個請求一樣進行動作

Web應用在搜索不同的 URL 使用不同的 HTTP 方法,默認的是,路徑只會響應 GET 請求,你可以使用 route() 裝飾器的 method 參數來處理不同的 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()

動態 Web 應用同樣需要靜態文件,通常是 JavaScript 和 CSS 文件。要創建靜態文件只需要在包裏面創建一個文件並取名爲 static ,就可以 /satatic 下使用。爲靜態文件創建 URL 可以使用 url_for() 函數,使用 static 這樣一個特殊的端點名稱。

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

這個文件必須在文件系統中存儲爲 static/style.css

Flask 使用爲你的應用配置一個 Jinja模板引擎。使用 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 文件中查找模板,因此如果你的應用是一個模塊,這個文件就在模塊旁邊,如果是一個包它就會在包裏

一個模板樣例如下

<!doctype html>
<title>Hello from Flask</title>
{% if name %}
  <h1>Hello {{ name }}!</h1>
{% else %}
  <h1>Hello, World!</h1>
{% endif %}

在模板裏面你同樣可以使用 request,session,和 g 對象,還有 get_flashed_messages() 函數。

request 對象記錄在 API 部分,要使用首先要從 flask 模塊中導入 request。

通過 request 的 method 屬性可以獲得當前請求的方法。通過 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'

    return render_template('login.html', error=error)

如果關鍵字在表單屬性中不存在,就會拋出特殊的 KeyError 異常,可以像標準的 KeyError 異常一樣對她進行捕獲,否則頁面就會顯示 400 Bad Request。因此大多數情況不需要處理這個問題。

通過 args 屬性獲得傳遞到 URL 中的參數 (?key=value)

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

推薦使用 get 或者捕獲 KeyError 獲得 URL 參數,因爲用戶可能改變 URL 或者不當的使用引發 400 Bad Request。request 對象更多的方法和屬性可以查閱 Request 文檔。

Flask 也可以很容易的處理上傳的文件,這個時候需要在 HTML 表單中設置 'enctype="multipart/form-data' ,否則瀏覽器不會爲你發送文件。通過 request 對象中的 file 屬性獲得這些文件,他就像標準的 python 文件對象一樣,但是他有 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.utils 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 並可以通過 response 對象的 set_cookie 方法設置 cookies。request 對象的 cookies 屬性是一個字典,包含了客戶端發送的所有 cookies。如果想要使用 sessions,就不要直接使用 cookies 而是使用 Flask 中的 Sessions ,可以爲 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

通過 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()

可以使用 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代表 Not Found。200 代表正常。

視圖函數的返回值會自動轉換爲 response 對象。如果返回值是一個字符串,它會轉換爲字符串作爲響應體的 response 對象,或者是一個 200 OK 的狀態碼或者是一個 文本/HTML mimetype。Flask 轉換返回值爲 response 對象的邏輯如下:

  1. 如果返回正確的 response 對象類型,就直接從視圖函數返回。
  2. 如果是一個字符串,就用那個數據和默認參數創建一個 response 對象。
  3. 如果返回一個元組,並且元組中的元素能夠提供額外的信息,這樣的元組必須放在表中(response,status,headers) 或者(response,headers) 也就是至少有一個元素放在表中。status 會重寫狀態碼,headers 可以是一個 列表 或者 字典。
  4. 如果上面情況都沒有出現,Flask 會認爲返回值是一有效的 WSGI 應用,並將之轉換爲一個 response 對象。

如果想要控制 view 裏面的 response 對象的結果,可以使用 make_response() 函數

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

例如上面的 view ,你只需要用 make_response()  重寫返回表達式,獲得 response 對象並修改它,然後將之返回即可。

@app.errorhandler(404)
def not_found(error):
    resp = make_response(render_template('error.html'), 404)
    resp.headers['X-Something'] = 'A value'
    return resp

除了 request 對象還有一種對象那就是 session 對象,它能夠從一個請求到另一個請求的特定用戶的信息。他以加密的方式在 cookies 上實現,這意味着用戶能夠看到你的 cookies 的內容但是不能修改它,除非以你知道密碼。

要使用 sessions 你必須設置密碼

from flask import Flask, session, redirect, url_for, escape, request

app = Flask(__name__)

# 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 '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 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'))

這裏的 escape() 是用於當你沒有使用模板引擎的時候跳出。

一個好的應用必須要處理好用戶反饋,Flask 提供了一個簡單的方式,也就是 flashing 系統,這個系統在請求最後記錄信息,能夠且僅能夠在下一個請求獲得它,這通常是和佈局模板結合起來暴露這個信息。通常需要使用的函數就是 flash() 以及如果想要控制這個信息要使用的 get_flash_messages() 函數

如果想要記錄日誌信息,可以使用 logger

app.logger.debug('A value for debugging')
app.logger.warning('A warning occurred (%d apples)', 42)
app.logger.error('An error occurred')

如果想要在應用中添加 WSGI 中間件,你也可以覆蓋掉內置的 WSGI 應用

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

此外,Flask 還有許多擴展,這些擴展通常是一些包能夠讓你完成一些通用的任務。例如,Flask-SQLAlchemy 提供了 SQLAlchemy 簡單易用的 Flask支持。

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