目录
一、代码实现
思路总结:若未登陆,则查询/userlist 、 /userdetail/1 、/时都会自动跳转/login界面。当session内存在user对象时才能正常访问
1-1 方式一、endpoint
from flask import Flask, request, render_template, redirect, session, url_for app = Flask(__name__) app.debug = True app.secret_key = 'test' # 模拟数据库内信息 USER_LIST = { 1: {'name': '张三', 'age': 18, 'gender': '男', 'text': "道路千万条"}, 2: {'name': '李四', 'age': 28, 'gender': '男', 'text': "安全第一条"}, 3: {'name': '王五', 'age': 18, 'gender': '女', 'text': "行车不规范"}, } def login_decorator(func, ): def wrapper(*args, **kwargs): if 'user' in session: res = func(*args, **kwargs) return res return redirect('/login') return wrapper @app.route('/') @login_decorator def index(): return 'ok' @app.route('/login', methods=['GET', 'POST'], endpoint='login') def login(): if request.method == 'GET': return render_template('login.html') else: # 取出表单提交的name数据 name = request.form.get('name') pwd = request.form.get('pwd') if name == 'test' and pwd == '123': # 登录成功 写session session['user'] = name return redirect('/userlist') else: # django中传字典,这里传**字典 # return render_template('login.html',**{'error':'用户名或密码错误'}) return render_template('login.html', error='用户名或密码错误') @app.route('/userlist', methods=['GET'], endpoint='user_list') @login_decorator def user_list(): return render_template('userlist.html', user_list=USER_LIST) @app.route('/userdetail/<int:nid>', methods=['GET'], endpoint='userdetail') @login_decorator def userdetail(nid): print(nid) user = USER_LIST.get(nid) return render_template('userdetail.html', user=user) if __name__ == '__main__': app.run()
1-2 方式二、装饰器内使用@wraps指向原有属性
from flask import Flask, request, render_template, redirect, session, url_for app = Flask(__name__) app.debug = True app.secret_key = 'test' # 模拟数据库内信息 USER_LIST = { 1: {'name': '张三', 'age': 18, 'gender': '男', 'text': "道路千万条"}, 2: {'name': '李四', 'age': 28, 'gender': '男', 'text': "安全第一条"}, 3: {'name': '王五', 'age': 18, 'gender': '女', 'text': "行车不规范"}, } from functools import wraps def login_decorator(func, ): @wraps(func) def wrapper(*args, **kwargs): if 'user' in session: res = func(*args, **kwargs) return res return redirect('/login') return wrapper @app.route('/') @login_decorator def index(): return 'ok' @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'GET': return render_template('login.html') else: # 取出表单提交的name数据 name = request.form.get('name') pwd = request.form.get('pwd') if name == 'test' and pwd == '123': # 登录成功 写session session['user'] = name return redirect('/userlist') else: # django中传字典,这里传**字典 # return render_template('login.html',**{'error':'用户名或密码错误'}) return render_template('login.html', error='用户名或密码错误') @app.route('/userlist', methods=['GET']) @login_decorator def user_list(): return render_template('userlist.html', user_list=USER_LIST) @app.route('/userdetail/<int:nid>', methods=['GET']) @login_decorator def userdetail(nid): print(nid) user = USER_LIST.get(nid) return render_template('userdetail.html', user=user) if __name__ == '__main__': app.run()
二、错误总结
错误分析
由于Flask框架在进行反向解析时,以映射方式进行查询,必须得保证函数别名的唯一性,否则会导致报错。即,route装饰器内endpoint参数必须唯一;而未指定endpoint参数时,框架默认将函数的__name__属性作为endpoint值进行返回。
当使用装饰器时,原功能函数的内部信息,会被装饰器的信息给覆盖。当存在多个函数使用同一个装饰器时,最后返回的信息都将相同,为装饰器的信息。
import time def outter1(func1): # print('outter1') def wrapper1(*args, **kwargs): # print('wrapper1') res1 = func1(*args, **kwargs) # res1=wrapper2(*args,**kwargs) return res1 return wrapper1 @outter1 def index(): ''' index 功能 ''' print('index page') time.sleep(2) print(index.__name__) # 显示装饰器内层函数名字 print(index.__doc__) # 显示装饰器的注释内容 ''' 使用装饰器之后 会显示 装饰器重新复制的函数对象数据 >>>wrapper1 >>>None '''
错误分析总结
当多个需要登入认证的函数,被认证装饰器包裹后,最后的__name__属性都将成为装饰器的内层函数名wrapper,并且被Flask默认赋予给endpoint属性进行处理。而endpoint属性并不允许重复,必须保持唯一,因而程序报错!
解决方式
1、手动指定endpoint,主动保持参数唯一性;2、装饰器内使用@wraps装饰内层,保证属性不覆盖。
三、@wraps 装饰器回顾
# ------ 不对内层函数使用 @ wraps() - --- import time def outter1(func1): # print('outter1') def wrapper1(*args, **kwargs): # print('wrapper1') res1 = func1(*args, **kwargs) # res1=wrapper2(*args,**kwargs) return res1 return wrapper1 @outter1 def index(): ''' index 功能 ''' print('index page') time.sleep(2) print(index.__name__) # 显示装饰器内层函数名字 print(index.__doc__) # 显示装饰器的注释内容 ''' 使用装饰器之后 会显示 装饰器重新复制的函数对象数据 >>>wrapper1 >>>None ''' # ------ 对内层函数使用 @ wraps() - ------------------------ import time from functools import wraps def outter1(func1): # 让使用装饰器的函数 调用__name__,__doc__的时候可以指向原有函数属性 # 若有多层函数嵌套,只需要在最内层的装饰器添加 @wraps @wraps(func1) def wrapper1(*args, **kwargs): # print('wrapper1') res1 = func1(*args, **kwargs) # res1=wrapper2(*args,**kwargs) return res1 return wrapper1 @outter1 def index(): ''' index 功能 ''' print('index page') time.sleep(2) print(index.__name__) # 显示函数名字 print(index.__doc__) # 显示函数的注释内容 ''' >>>index >>> >>> index 功能 >>> '''