Flask 添加登陆验证装饰器报错,及解析
写这个之前,是想到一个需求,这个是关于之前写Flask笔记(二)
中的一个知识点,路由相关
需求为
: 有一些页面必须是登陆之后才能访问的,比如ShoppingCart,说白了就是写一个登陆验证,那怎么才能知道当前这个请求
就是登陆状态的呢?如果不是登陆状态,那应该重定向到login页面
from flask import Flask,request,session,redirect,url_for
app = Flask(__name__)
# 如果要使用session,必须为SECRET_KEY设置一个值,原因我之后会在session和cookie那里讲
app.config['SECRET_KEY'] = 'zhuchunyudashuaibi'
def auth_login(function):
"""登陆验证装饰器,如果是登陆状态的话,is_login为真"""
def inner(*args,**kwargs):
is_login = session.get("is_login",None)
if is_login:
return function(*args,**kwargs)
else:
url = url_for("login")
return redirect(url)
return inner
@app.route('/shopcart',methods = ["GET","POST"])
@auth_login
def shopCart():
'''购物车页面'''
return "shopCart pages"
@app.route("/login",methods=["GET","POST"])
def login():
if request.method == "POST":
name = request.form.get("name")
password = request.form.get("password")
if name == "朱宇" and password == "123":
session["name"] = name
session["password"] = password
session["is_login"] = True
return " login sucess !! "
else:
return " login faild "
return 'login.html'
if __name__ == "__main__":
app.run()
启动测试一下,下面是截图,表示设置成功了
但是我再基于上面的代码,再加一个视图函数,pay
视图函数吧。下面是代码
@app.route('/pay',methods = ["GET","POST"])
@auth_login
def pay():
'''支付页面'''
return "pay pages"
再次启动一下,你会发现这时候就报错了
报错的原因是:inner
这个函数重复了,我们在哪里写了inner
函数?对,没错就是登陆验证装饰器
先看我写的一段测试代码
看到它的打印效果了嘛,这样就可以说明问题
- 先是
decorator_a
这个函数,将f作为参数传递,得到这样的结果inner_a
=decorator_a(f)
- 再将
inner_a
最为参数传递给decorator_b
最后得到inner_b
# 我们现在来仔细的研究下这段代码
@app.route('/pay',methods = ["GET","POST"])
@auth_login
def pay():
'''支付页面'''
return "pay pages"
-
pay
函数作为参数传递给auth_login
,便会执行auth_login
函数,得到返回值inner
函数,上面代码可以改成下面这个样子@app.route('/pay',methods = ["GET","POST"]) inner函数的内存地址
-
@app.route('/pay',methods = ["GET","POST"])
,route它是直接加了括号的,所以应该是@decorator
, 那么会将inner
传递给函数decorator
,便会执行decorator
。def decorator(f): endpoint = options.pop('endpoint', None) self.add_url_rule(rule, endpoint, f, **options) return f
这里的参数
f
就是 函数inner
既然写到这里了,那就继续看
self.add_url_rule(rule, endpoint, f, **options)
,我先说这个参数现在是什么值 ,rule='/pay'
,endpoint=None
因为app.route
方法没有传递该参数,所以为None,f=inner
,记住参数的值,我们看
add_url_rule
的部分源码,只看endpoint这部分。def add_url_rule(self, rule, endpoint=None, view_func=None,provide_automatic_options=None, **options): if endpoint is None: endpoint = _endpoint_from_view_func(view_func) options['endpoint'] = endpoint
判断
endpoint
为None的话,便会将_endpoint_from_view_func(view_func)
的返回值赋值给endpoint
,最后以key为endpoint(这里是一个字典的key值,一个字符串)
, 将变量endpoint
为values值,view_func
这个变量就是inner
函数,继续看_endpoint_from_view_func
方法def _endpoint_from_view_func(view_func): """Internal helper that returns the default endpoint for a given function. This always is the function name. """ assert view_func is not None, 'expected view func if endpoint ' 'is not provided.' return view_func.__name__
最终返回了函数的
__name__
的值,所以endpoint=inner(这个inner是一个字符串了)
最终它是以endpoint
的值来进行url和视图函数之间的对应关系的,怎么证明?很简单,我们回到最初时候的代码,flask程序没有报错之前,在app.run()
前一行加这样一段代码print(app.url_map())
它的作用就是打印出url映射关系。我只为
shopCart
加了登陆验证装饰器,它所对应的视图函数为inner
,再看其他的Rule
,app.route
我都没有设置endpoint
的值,所以都是对应想应的视图函数的__name__
。
好了,解释了一大推报错的原因,那么我们怎么改,才不会有BUG呢?我们为pay
函数加了登陆验证的装饰器,这跟为shopCart
加不是一样么,出现了两个inner
的对应关系,所以报错了。我们只需为这个两个视图函数添加不同的endpoint
值就好了,最好是有标识性的,反向解析可能也用得上。下面是改进的代码
from flask import Flask,request,session,redirect,url_for
app = Flask(__name__)
# 如果要使用session,必须为SECRET_KEY设置一个值,原因我之后会在session和cookie那里讲
app.config['SECRET_KEY'] = 'zhuchunyudashuaibi'
def auth_login(function):
"""登陆验证装饰器,如果是登陆状态的话,is_login为真"""
def inner(*args,**kwargs):
is_login = session.get("is_login",None)
if is_login:
return function(*args,**kwargs)
else:
url = url_for("login")
return redirect(url)
return inner
@app.route('/shopCart', methods=["GET", "POST"],endpoint='shopCart')
@auth_login
def shopCart():
'''购物车页面'''
return "shopCart pages"
@app.route('/pay', methods=["GET", "POST"],endpoint='pay')
@auth_login
def pay():
'''支付页面'''
return "pay pages"
@app.route("/login",methods=["GET","POST"])
def login():
if request.method == "POST":
name = request.form.get("name")
password = request.form.get("password")
if name == "朱宇" and password == "123":
session["name"] = name
session["password"] = password
session["is_login"] = True
return " login sucess !! "
else:
return " login faild "
return 'login.html'
if __name__ == "__main__":
app.run()
好了,这个知识点就总结完了,希望看到的同学,对你有所帮助吧!