上一次咱們作了一個簡單的在線股票走勢網站,今天咱們來繼續完善下網站功能,並學習些新的 Flask 知識點。html
咱們先來看下上一篇中獲取表單的寫法前端
stock_name = request.form.get('stockName')
query_time = request.form.get('queryTime')
複製代碼
這裏用到了 request,其實它就是一種請求上下文。json
那麼什麼是請求上下文呢,其實就是當 Flask 程序初始化成功後,每次請求中的全局變量。請求上下文總共有兩個,request 和 session。flask
從上面的代碼咱們能夠想象獲得,request 變量當中應該是包含了本次 HTTP 請求中的相關信息,好比 form 屬性中就是前端提交的表單數據,固然還有些其餘屬性和方法,我整理以下: URL 信息相關,例如請求 URL 爲:www.luobodazahui.top/hello?name=…bootstrap
屬性 | 值 |
---|---|
path | '/hello' |
full_path | '/hello?name=zhouluobo' |
host | 'www.luobodazahui.top' |
host_url | 'www.luobodazahui.top' |
base_url | 'www.luobodazahui.top/hello' |
url | 'www.luobodazahui.top/hello?name=…' |
報文相關信息瀏覽器
屬性或方法 | 說明 |
---|---|
args | 查詢字符串信息 |
cookies | cookies 信息字典 |
data | 字符串形式的請求數據 |
form | 表單數據 |
get_json() | 獲取 json 類型的請求數據 |
method | 請求的 HTTP 方法 |
那麼 session 呢,其實就是用於存儲請求之間須要保留的數據,比較典型的應用場景就是用戶的認證功能。安全
下面咱們就結合 request 和 session 兩個請求上下文,在當前網站的基礎上,來動手實現一個簡單的認證功能。bash
咱們當前網站的股票歷史數據查詢時間是能夠自行定義的,那麼咱們能夠增長一個限制,就是非登錄用戶只能夠查詢30天之內的數據,而對於已經登錄的用戶,則不受該限制約束。cookie
那麼咱們就須要一個簡單的登錄頁面,進行登錄操做,固然最重要的就是應用 session 這個請求上下文來判斷用戶登錄狀態了。session
首先先實現功能限制處理,把未登錄用戶輸入大於30天的請求攔截,並提示須要登錄後再嘗試
修改 get_kline_chart() 函數,增長一個判斷,若是 query_time 大於30,則返回403響應碼
@app.route("/Kline", methods=['GET', 'POST'])
def get_kline_chart():
stock_name = request.form.get('stockName')
query_time = request.form.get('queryTime')
if int(query_time) > 30:
abort(403)
...
複製代碼
再修改 JQuery 的 getData 函數中的異常部分
error: function(err) {
if (err.status === 403) {
alert("請先登錄系統!");
}
else {
alert("錯誤的股票代碼!");
}
}
複製代碼
接下來就來添加登錄入口,首先在導航欄上添加一個登錄的連接
<ul class="nav navbar-nav navbar-right">
<li><a href="{{ url_for('login') }}">Log In</a></li>
<li><a href="{{ url_for('logout') }}">Log Out</a></li>
</ul>
複製代碼
兩個連接分別對應到登錄和登出的視圖函數
建立登錄登出函數,應用 session 變量來傳遞用戶狀態
@app.route('/login/')
def login():
session['login_user'] = 'admin'
return redirect(url_for('index'))
@app.route('/logout')
def logout():
if 'login_user' in session:
session.pop('login_user')
return redirect(url_for('index'))
複製代碼
此時咱們刷新頁面,查看當前的頁面展現
<ul class="nav navbar-nav navbar-right">
{% if not auth %}
<li><a href="{{ url_for('login') }}">Log In</a></li>
{% else %}
<li><a href="{{ url_for('logout') }}">Log Out</a></li>
{% endif %}
</ul>
複製代碼
作一個判斷,若是 auth 是 False 時,展現 Login In,若是是 True 時,展現 Log Out。
再修改 index 視圖函數,判斷用戶狀態,並返回 auth 變量到模板
@app.route("/")
def index():
auth = False
if 'login_user' in session:
auth = True
return render_template("index.html", auth=auth)
複製代碼
當前的登錄是直接寫入 cookie 的,並無用戶輸入表單的登錄過程,如今完成這個過程。
咱們使用 Flask-WTF 來快速建立表單
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired
class LoginForm(FlaskForm):
name = StringField('name', validators=[DataRequired()])
password = PasswordField('password', validators=[DataRequired()])
submit = SubmitField('Submit')
複製代碼
接下來咱們建立一個 login.html 文件,並把表單渲染成 HTML
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}登錄{% endblock %}
{% block page_content %}
{{ wtf.quick_form(form) }}
{% endblock %}
複製代碼
接下來咱們修改 login 視圖函數,接收表單數據,並驗證用戶
def check_name(name, password):
return True
@app.route('/login/', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
name = form.name.data
password = form.password.data
if check_name(name, password):
session['login_user'] = 'admin'
return redirect(url_for('index'))
return render_template('login.html', form=form)
複製代碼
這裏的驗證用戶函數我沒有作任何邏輯,因此不管輸入什麼用戶名和密碼,都會驗證經過並登錄成功。
最後一步,咱們就能夠在 get_kline_chart 函數中驗證當前用戶是否已經登錄過了,若是是,則正常操做
if int(query_time) > 30:
if 'login_user' in session:
pass
else:
abort(403)
複製代碼
至此,咱們就完成了基於請求上下文 session 的簡單認證功能。
如今咱們再來回顧下 session 上下文到底爲咱們作了些什麼
當咱們完成登錄操做後,能夠查看瀏覽器中的 cookie 信息,能夠發現咱們經過 session 設置的 cookie 信息已經被加密了,這極大的提升了咱們應用的安全性
app.secret_key = 'A Hard String'
複製代碼
接下來咱們再來看看另外一種 Flask 上下文--程序上下文。程序上下文主要包含兩種,current_app 和 g,current_app 就是當前的程序實例,而 g 則能夠臨時存儲當前請求的數據,方便使用。
對於 current_app 這個程序上下文,主要的用途在於當程序當中存在多個程序實例時,使用該上下文能夠方便的獲取到當前的程序實例,通常在編寫大型應用時會用到,咱們在後面的學習中用到時再詳細介紹。
對於 g 這個上下文變量來講,其用途會更加普遍些。好比說若是對於某個請求,咱們幾個視圖函數都須要用到一個前端傳遞過來的變量,那麼就能夠把它保存到 g 變量當中
g.name = request.args.get('name')
複製代碼
這樣,其餘的視圖函數就能夠在同一個請求中直接使用 g.name 來訪問,而不用每次都調用 request 了。而這種特性每每和請求鉤子相結合使用,能夠極大的提升代碼的簡潔性。
嗯,好的,今天的分享就到這裏了,咱們下次再見!