• flask笔记(三)Flask 添加登陆验证装饰器报错,及解析


    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 函数?对,没错就是登陆验证装饰器

    先看我写的一段测试代码

    看到它的打印效果了嘛,这样就可以说明问题

    1. 先是decorator_a这个函数,将f作为参数传递,得到这样的结果 inner_a = decorator_a(f)
    2. 再将inner_a最为参数传递给 decorator_b 最后得到inner_b
    # 我们现在来仔细的研究下这段代码
    @app.route('/pay',methods = ["GET","POST"])
    @auth_login
    def pay():
    	'''支付页面'''
    	return "pay pages"
    
    
    1. pay 函数作为参数传递给auth_login ,便会执行auth_login 函数,得到返回值 inner 函数,上面代码可以改成下面这个样子

      @app.route('/pay',methods = ["GET","POST"])
      inner函数的内存地址
      
    2. @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 ,再看其他的Ruleapp.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()
    

    好了,这个知识点就总结完了,希望看到的同学,对你有所帮助吧!

  • 相关阅读:
    CentOS安装Nginx Pre-Built
    CMake设置编译参数
    SQLServer脚本编写
    使用QNetworkAccessManager实现Qt的FTP下载服务
    使用CMD命令设置IP
    IIS6(Win2003) 使用.net 4.0 后,默认文档失效解决方案。
    windows7打印时,显示脱机,提示“服务器打印后台处理程序服务没有运行”。
    阻止浏览器自动填表
    Java经典编程题50道之四
    Java经典编程题50道之三
  • 原文地址:https://www.cnblogs.com/zhuchunyu/p/10466504.html
Copyright © 2020-2023  润新知