Flask - 练习 - 基于装饰器实现登陆验证、及装饰器与endpoint报错分析

目录

一、代码实现

1-1 方式一、endpoint 

1-2 方式二、装饰器内使用@wraps指向原有属性

二、错误总结

错误分析

错误分析总结

解决方式

三、@wraps 装饰器回顾


一、代码实现

思路总结:若未登陆,则查询/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 功能
>>>
'''

猜你喜欢

转载自blog.csdn.net/qq_33961117/article/details/87363328