我們已經寫好驗證的 views,但是如果現在啟動服務的話
無論開啟哪個 URL,都會看到一個TemplateNotFound
錯誤
這是因為 view 使用了render_template()
,但是 template 檔案還沒有寫
模板檔案會存放在 flaskr 資料夾內的templates
文件夾內
Flask 使用Jinja模板引擎來渲染模板,範例中會使用模板來顯示在使用者瀏覽器中的 HTML
在 Flask 中, Jinja 被設定為自動轉譯 HTML 模板中的任何資料,這代表直接渲染內容是安全的
任何使用者輸入的可能被瀏覽器執行的內容,如<
和>
,會被轉譯成安全的值
轉譯的結果在瀏覽器中看起來一樣,但是不會被瀏覽器執行
Jinja 看上去以及執行起來很像 Python
在 Jinja 中,任何位於{{
和}}
之間的變數會被輸出成文字
程式判斷式則是放在{%
和%}
之間,例如if
和for
與 Python 不同,不是使用縮排分隔,而是使用特殊 tag 分隔
因為區塊內的渲染出來的結果可能有自己的縮排
與其在不同的頁面中重寫整個頁面的 HTML,應該讓每一頁的模板繼承一個基本的共用模板
接著在不同頁面中複寫需要修改的部分
flaskr/templates/base.html
<!doctype html>
<title>{% block title %}{% endblock %} - Flaskr</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<nav>
<h1>Flaskr</h1>
<ul>
{% if g.user %}
<li><span>{{ g.user['username'] }}</span>
<li><a href="{{ url_for('auth.logout') }}">Log Out</a>
{% else %}
<li><a href="{{ url_for('auth.register') }}">Register</a>
<li><a href="{{ url_for('auth.login') }}">Log In</a>
{% endif %}
</ul>
</nav>
<section class="content">
<header>
{% block header %}{% endblock %}
</header>
{% for message in get_flashed_messages() %}
<div class="flash">{{ message }}</div>
{% endfor %}
{% block content %}{% endblock %}
</section>
g 在模板中自動可用!
根據g.user
是否被設定(記得嗎?之前在load_logged_in_user
中進行)
選擇要顯示使用者名稱和登出按鈕,還是註冊和登入按鈕?
而用於生成 view 網址的url_for()
也是自動可用的,不需要手動指定
在頁面標題下面,content 內容的前面,模板會循環顯示 get_flashed_messages() 回傳的每個消息
可以用這個方式把之前在 view 中使用 flash() 保存的錯誤訊息顯示出來
There are three blocks defined here that will be overridden in the other templates:
模板中定義了三個 block,這些區塊內容會被其他模板複寫
{% block title %}
會改變當前瀏覽器分頁的 title
{% block header %}
改變頁面的標題
{% block content %}
每個頁面的具體內容,例如登入表單或者部落格文章
其他模板直接放在templates
資料夾內
為了更好地管理檔案,屬於某個 blueprints 的模板會被放在與同名的資料夾內
flaskr/templates/auth/register.html
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Register{% endblock %}</h1>
{% endblock %}
{% block content %}
<form method="post">
<label for="username">Username</label>
<input name="username" id="username" required>
<label for="password">Password</label>
<input type="password" name="password" id="password" required>
<input type="submit" value="Register">
</form>
{% endblock %}
開頭使用{% extends 'base.html' %}
對 Jinja 宣告這個模板繼承 base.html
並且需要載入相應的 block,所有複寫的內容必須位於{% block %}
標籤中
一個好用的模式是把{% block title %}
放在{% block header %}
內部
這樣不但可以設定 title block,還可以把其值作為 header block 的內容,一舉兩得!
input
tag 使用了required
屬性,告訴瀏覽器這些輸入框是必填的
如果使用者使用不支援持這個屬性的舊版瀏覽器,或者不是用瀏覽器發出的請求
那麼你還是要在 view 中驗證輸入資料
即使前端已經做了一些驗證,後端還是要完全檢查,這一點非常重要!
這個模板除了標題和送出按鈕外,和註冊模板相同
flaskr/templates/auth/login.html
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Log In{% endblock %}</h1>
{% endblock %}
{% block content %}
<form method="post">
<label for="username">Username</label>
<input name="username" id="username" required>
<label for="password">Password</label>
<input type="password" name="password" id="password" required>
<input type="submit" value="Log In">
</form>
{% endblock %}
現在驗證模板已寫好,可以來測試了
啟動測試環境之後開啟http://127.0.0.1:5000/auth/register
頁面
在不填寫表單的情況,嘗試點擊「Register」按鈕,瀏覽器會顯示出錯信息
嘗試在register.html
中刪除required
屬性後再次點擊「Register」按鈕
瀏覽器不會顯示錯誤,而頁面會重新載入並顯示來自於 view 中 flash() 錯誤訊息
填寫使用者名稱和密碼後會重定導向到登錄頁面
嘗試輸入錯誤的使用者名稱,或者輸入正確的使用者名稱和錯誤的密碼
真實情況下不要這麼做!會引發資安疑慮,弱點掃描不會過
因為提示駭客錯誤是來自哪個欄位,接著就可以使用枚舉法暴力破解
如果登錄成功,那麼會看到一個錯誤訊息
因為我們還沒有寫登入後要轉向的 view:index