Flask 扫盲系列-Flask 上下文

1,897 阅读5分钟

上一次我们做了一个简单的在线股票走势网站,今天我们来继续完善下网站功能,并学习些新的 Flask 知识点。

请求上下文

我们先来看下上一篇中获取表单的写法

stock_name = request.form.get('stockName')
query_time = request.form.get('queryTime')

这里用到了 request,其实它就是一种请求上下文。

那么什么是请求上下文呢,其实就是当 Flask 程序初始化成功后,每次请求中的全局变量。请求上下文总共有两个,request 和 session。

从上面的代码我们可以想象得到,request 变量当中应该是包含了本次 HTTP 请求中的相关信息,比如 form 属性中就是前端提交的表单数据,当然还有些其他属性和方法,我整理如下: URL 信息相关,例如请求 URL 为:www.luobodazahui.top/hello?name=…

属性
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 两个请求上下文,在当前网站的基础上,来动手实现一个简单的认证功能。

功能需求整理

我们当前网站的股票历史数据查询时间是可以自行定义的,那么我们可以增加一个限制,就是非登陆用户只可以查询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 上下文到底为我们做了些什么

  1. 设置 cookie,用于传递信息
  2. 自动加密,加大篡改难度

当我们完成登陆操作后,可以查看浏览器中的 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 了。而这种特性往往和请求钩子相结合使用,可以极大的提高代码的简洁性。

嗯,好的,今天的分享就到这里了,我们下次再见!