上一次我们做了一个简单的在线股票走势网站,今天我们来继续完善下网站功能,并学习些新的 Flask 知识点。
请求上下文
我们先来看下上一篇中获取表单的写法
stock_name = request.form.get('stockName')
query_time = request.form.get('queryTime')
这里用到了 request,其实它就是一种请求上下文。
那么什么是请求上下文呢,其实就是当 Flask 程序初始化成功后,每次请求中的全局变量。请求上下文总共有两个,request 和 session。
从上面的代码我们可以想象得到,request 变量当中应该是包含了本次 HTTP 请求中的相关信息,比如 form 属性中就是前端提交的表单数据,当然还有些其他属性和方法,我整理如下:
URL 信息相关,例如请求 URL 为:http://www.luobodazahui.top/hello?name=zhouluobo
属性 | 值 |
---|---|
path | ‘/hello’ |
full_path | ‘/hello?name=zhouluobo’ |
host | ‘www.luobodazahui.top’ |
host_url | ‘http://www.luobodazahui.top’ |
base_url | ‘http://www.luobodazahui.top/hello’ |
url | ‘http://www.luobodazahui.top/hello?name=zhouluobo’ |
报文相关信息
属性或方法 | 说明 |
---|---|
args | 查询字符串信息 |
cookies | cookies 信息字典 |
data | 字符串形式的请求数据 |
form | 表单数据 |
get_json() | 获取 json 类型的请求数据 |
method | 请求的 HTTP 方法 |
那么 session 呢,其实就是用于存储请求之间需要保留的数据,比较典型的应用场景就是用户的认证功能。
下面我们就结合 request 和 session 两个请求上下文,在当前网站的基础上,来动手实现一个简单的认证功能。
功能需求整理
我们当前网站的股票历史数据查询时间是可以自行定义的,那么我们可以增加一个限制,就是非登陆用户只可以查询30天以内的数据,而对于已经登陆的用户,则不受该限制约束。
那么我们就需要一个简单的登陆页面,进行登陆操作,当然最重要的就是应用 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'))
此时我们刷新页面,查看当前的页面展示
可以看到无论用户登陆与否,都会展示 login 和 logout 两个按钮,这很不美观,我们再做下修改
<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,用于传递信息
- 自动加密,加大篡改难度
当我们完成登陆操作后,可以查看浏览器中的 cookie 信息,可以发现我们通过 session 设置的 cookie 信息已经被加密了,这极大的提高了我们应用的安全性
而这个加密的 key,我们可以通过初始化的 app 的方法来设置
app.secret_key = 'A Hard String'
程序上下文
接下来我们再来看看另一种 Flask 上下文–程序上下文。程序上下文主要包含两种,current_app 和 g,current_app 就是当前的程序实例,而 g 则可以临时存储当前请求的数据,方便使用。
current_app
对于 current_app 这个程序上下文,主要的用途在于当程序当中存在多个程序实例时,使用该上下文可以方便的获取到当前的程序实例,一般在编写大型应用时会用到,我们在后面的学习中用到时再详细介绍。
g
对于 g 这个上下文变量来说,其用途会更加广泛些。比如说如果对于某个请求,我们几个视图函数都需要用到一个前端传递过来的变量,那么就可以把它保存到 g 变量当中
g.name = request.args.get('name')
这样,其他的视图函数就可以在同一个请求中直接使用 g.name 来访问,而不用每次都调用 request 了。而这种特性往往和请求钩子相结合使用,可以极大的提高代码的简洁性。
嗯,好的,今天的分享就到这里了,我们下次再见!