• [Python自学] Flask框架 (1) (Flask介绍、配置、Session、路由、请求和响应、Jinjia2模板语言、视图装饰器)


    oldboy:s9day114

    参考博客:https://www.cnblogs.com/wupeiqi/articles/7552008.html

    一、Flask简介

    1.安装Flask

    pip install flask

    Flask:

      - 短小精悍、可扩展性强的一个Web框架。

      - 依赖wsgi:werkzurg(安装Flask时,这些依赖也会被自动安装)

    2.Werkzurg服务器

    单独使用Werkzurg服务器:

    from werkzeug.wrappers import Request, Response
    from werkzeug.serving import run_simple
    
    
    @Request.application
    def run(request):
        return Response('Hello World')
    
    
    if __name__ == '__main__':
        run_simple('localhost', 4000, run)

    3.实现简单页面请求

    from flask import Flask
    
    # 创建一个Flask实例,参数是为这个实例定义一个名字,一般以__name__来指定
    app = Flask(__name__)
    
    
    # 利用装饰器,绑定路由
    @app.route('/index')
    def index():
        return "HelloWorld"
    
    
    if __name__ == '__main__':
        # 开始运行
        app.run()

    二、简单使用Flask

    1.默认模板目录

    Flask默认使用templates作为模板目录。当我们要返回html页面时,默认去该文件夹下找模板文件:

    from flask import Flask, render_template
    
    # 创建一个Flask实例,参数是为这个实例定义一个名字,一般以__name__来指定
    app = Flask(__name__)
    
    
    @app.route('/login')
    def login():
        return render_template('login.html')
    
    
    if __name__ == '__main__':
        # 开始运行
        app.run()

    2.修改模板目录

    在Django中,我们是在setting配置文件中去修改。而在Flask中,我们可以以以下方式修改:

    # 创建一个Flask实例,参数是为这个实例定义一个名字,一般以__name__来指定
    app = Flask(__name__, template_folder='mytempfile')

    我们查看一下Flask类的构造函数:

    def __init__(
        self,
        import_name,
        static_url_path=None,
        static_folder='static',
        static_host=None,
        host_matching=False,
        subdomain_matching=False,
        template_folder='templates',
        instance_path=None,
        instance_relative_config=False,
        root_path=None
    ):

    可以看到,默认的模板目录参数就是"templates"。

    3.Flask接受POST请求、获取请求数据

    在Flask中,默认只允许接收GET请求,如果我们先接收POST请求,则需要设置。

    在视图函数中获取请求数据时,我们需要导入request模块,而不是通过参数传递request。这是个Django不同的地方。

    from flask import Flask, render_template, request
    
    app = Flask(__name__)
    
    
    @app.route('/login', methods=['GET', 'POST'])
    def login():
        # 这里的请求数据和Django中不一样,Flask是通过上下文来管理的,request不是参数,而是导入的模块
        if request.method == 'GET':
            pass
    
        if request.method == 'POST':
            pass
    
    
    if __name__ == '__main__':
        # 开始运行
        app.run()

    4.获取GET数据和POST数据

    视图函数:

    from flask import Flask, render_template, request, redirect
    
    app = Flask(__name__)
    
    
    @app.route('/login', methods=['GET', 'POST'])
    def login():
        # 这里的请求数据和Django中不一样,Flask是通过上下文来管理的,request不是参数,而是导入的模块
        if request.method == 'GET':
            # request.args对应django中的request.GET
            # request.args.get('user)
            return render_template('login.html')
        if request.method == 'POST':
            user = request.form.get('user')
            pwd = request.form.get('pwd')
            if user == 'leokale' and pwd == '123':
                return redirect('/index')
            else:
                return render_template('login.html', error="用户名或密码错误")
                # 也可以传多个值(使用字典),但和Django不一样的地方是要解包
                # return render_template('login.html', **{"error": "用户名或密码错误"})
    
    
    @app.route('/index')
    def index():
        return '<h2>欢迎登录</h2>'
    
    
    if __name__ == '__main__':
        # 开始运行
        app.run()

    注意几个和django框架不一样的地方:

    1)需要在装饰器中传入methods参数,用来允许接收哪些请求

    2)request.args和request.form分别对应django中的request.GET和request.POST

    3)redirect跳转和django基本一致

    4)模板渲染回传给页面的参数,可以单独传一个,也可以使用字典拆包的形式传入多个(django中参数为字典)

    对应html代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Login</title>
    </head>
    <body>
        <h1>登录页面</h1>
        <form  method="POST">
            <input type="text" name="user"/>
            <input type="password" name="pwd"/>
            <input type="submit" value="登录"/>{{error}}
     </form> </body> </html>

    5.静态文件

    Flask中静态文件默认放在static目录下,如果要修改,也是修改Flask实例化时的参数。

    app = Flask(__name__,static_folder='mystatic')

    只需要将目录名和这个参数值对应上即可。

    但是注意,我们在html中写图片请求连接时使用的url默认是和static_folder一致的:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>INDEX</title>
    </head>
    <body>
        <h2>欢迎访问</h2>
        <img src="/mystatic/111.png">
    </body>
    </html>

    但实际上,我们也可以通过一个参数来修改:

    app = Flask(__name__, static_folder='mystatic', static_url_path='/vvvvv')

    我们将其修改为'/vvvvv',此时html中也要修改为'/vvvvv':

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>INDEX</title>
    </head>
    <body>
        <h2>欢迎访问</h2>
        <img src="/vvvvv/111.png">
    </body>
    </html>

    6.Flask中的session

    from flask import Flask, render_template, request, redirect, session
    
    app = Flask(__name__, static_folder='mystatic', static_url_path='/vvvvv')
    
    
    @app.route('/login', methods=['GET', 'POST'])
    def login():
        # 这里的请求数据和Django中不一样,Flask是通过上下文来管理的,request不是参数,而是导入的模块
        if request.method == 'GET':
            # request.args对应django中的request.GET
            # request.args.get('user)
            return render_template('login.html')
        if request.method == 'POST':
            user = request.form.get('user')
            pwd = request.form.get('pwd')
            if user == 'leokale' and pwd == '123':
                session['user'] = user
                return redirect('/index')
            else:
                return render_template('login.html', error="用户名或密码错误")
                # 也可以传多个值(使用字典),但和Django不一样的地方是要解包
                # return render_template('login.html', **{"error": "用户名或密码错误"})
    
    
    @app.route('/index')
    def index():
        user = session.get('user')
        if not user:
            return redirect('/login')
        return render_template('index.html')
    
    
    if __name__ == '__main__':
        # 开始运行
        app.run()

    Flask中session的使用和django很类似,但Flask中的session是导入的模块。

    注意,Flask的session默认是保存在加密的cookie中的,可以参照django相关的介绍:https://www.cnblogs.com/leokale-zz/p/12082478.html

    这里要使用加密的cookie,又需要设置MD5加盐中的盐,否则会报错:

    RuntimeError: The session is unavailable because no secret key was set.  Set the secret_key on the application to something unique and secret.
    127.0.0.1 - - [20/Jan/2020 00:27:46] "POST /login HTTP/1.1" 500 -

    我们需要在代码中加上加密设置:

    app = Flask(__name__, static_folder='mystatic', static_url_path='/vvvvv')
    app.secret_key = "sndjkfn"  # MD5加密的盐

    此时就可以使用加密cookie来保存session了。

    三、Flask的配置文件

    1.通过importlib导入指定模块

    在setting.py文件中有如下代码:

    class Foo(object):
        DEBUG = True
        TEST = False

    我们如果在其他模块中通过importlib导入Foo类,获取其中的DEBUG和TEST的值:

    import importlib
    
    # 给定类所处模块
    path = 'setting.Foo'
    
    # 分割path,获取模块名和类名
    (m, c) = path.rsplit('.', maxsplit=1)
    # 导入模块
    m = importlib.import_module(m)
    # 获取类
    cls = getattr(m, c)
    
    # 遍历类中的属性,我们要获取的属性是大写的,所以使用isupper判断
    for key in dir(cls):
        if key.isupper():
            print(key, getattr(cls, key))

    这样,我们就使用importlib库动态的导入模块,并获取了其中的Foo类,以及其中的类属性。

    Flask的配置文件也是通过这种方式来加载的(很多框架的配置文件都是通过这种方式加载)。

    2.Flask默认的配置信息

    Flask默认的配置信息:

    from flask import Flask, request, render_template
    
    app = Flask(__name__)
    # app.config获取所有配置信息
    print(app.config)
    # 可以通过这种方式修改
    app.config['DEBUG'] = True

    app.config配置内容:

    {
        'ENV': 'development',
        'DEBUG': False,
        'TESTING': False,
        'PROPAGATE_EXCEPTIONS': None,
        'PRESERVE_CONTEXT_ON_EXCEPTION': None,
        'SECRET_KEY': None,
        'PERMANENT_SESSION_LIFETIME': datetime.timedelta(days = 31),  # 默认session的保存周期(对应加密cookie的过期时间)
        'USE_X_SENDFILE': False,
        'SERVER_NAME': None,
        'APPLICATION_ROOT': '/',
        'SESSION_COOKIE_NAME': 'session',  # 加密session写到cookie中的key,默认叫session
        'SESSION_COOKIE_DOMAIN': None,
        'SESSION_COOKIE_PATH': None,
        'SESSION_COOKIE_HTTPONLY': True,
        'SESSION_COOKIE_SECURE': False,
        'SESSION_COOKIE_SAMESITE': None,
        'SESSION_REFRESH_EACH_REQUEST': True,  # 是否在每次访问的时候刷新session过期时间(即从最后一次访问开始计算过期时间),如果为False,则过期时间时从设置session开始算
        'MAX_CONTENT_LENGTH': None,
        'SEND_FILE_MAX_AGE_DEFAULT': datetime.timedelta(seconds = 43200),
        'TRAP_BAD_REQUEST_ERRORS': None,
        'TRAP_HTTP_EXCEPTIONS': False,
        'EXPLAIN_TEMPLATE_LOADING': False,
        'PREFERRED_URL_SCHEME': 'http',
        'JSON_AS_ASCII': True,
        'JSON_SORT_KEYS': True,
        'JSONIFY_PRETTYPRINT_REGULAR': False,
        'JSONIFY_MIMETYPE': 'application/json',
        'TEMPLATES_AUTO_RELOAD': None,
        'MAX_COOKIE_SIZE': 4093
    }

    3.使用自定义配置文件

    使用以下方式为其指定一个自定义配置文件:

    from flask import Flask, request, render_template
    
    app = Flask(__name__)
    # app.config获取DEBUG,默认为False
    print(app.config['DEBUG'])
    # 通过from_object绑定一个类作为配置对象,其中DEBUG为True
    app.config.from_object("setting.Myconfig")
    # 再次获取DEBUG,为True
    print(app.config['DEBUG'])

    这里使用setting模块中的Myconfig类作为其配置文件。from_object源码就是使用的1.中的方式对setting模块进行导入,然后获取Myconfig中的属性值。

    setting模块中,我们还可以根据需求,使用多个类来区分不同场景下的配置:

    class BaseConfig(object):
        # DEV和PROD场景下,相同的配置
        XXX = False
    
    
    # 开发环境下的配置
    class DevConfig(BaseConfig):
        DEBUG = True
        TEST = True
    
    
    # 生产环境下的配置
    class ProdConfig(BaseConfig):
        DEBUG = False
        TEST = False

    在使用的时候,我们只需要根据场景切换即可:

    # 开发环境使用DevConfig配置
    app.config.from_object("setting.DevConfig")
    
    # 生产部署环境使用ProdConfig配置
    app.config.from_object("setting.ProdConfig")

    4.其他配置文件的使用

    除了3.中所描述的,使用模块中的类来作为配置。还有以下几种方式:

    1)from_pyfile

    app.config.from_pyfile("setting.py")

    例如:

    # settings.py
                    
    DEBUG = True
    TEST = False

    2)from_envvar

    app.config.from_envvar("FLASK_CONFIG_FILE")

    例如在Linux下配置环境变量:

    export FLASK_CONFIG_FILE='/path/to/config/file'

    环境变量的值为python文件(不一定是.py结尾的文件,但内容要是 DEBUG = True这种Python代码格式)名称,内部调用from_pyfile方法。

    3)from_json

    app.config.from_json("json文件名称")

    JSON文件名称,必须是json格式,因为内部会执行json.loads。

    4)from_mapping

    app.config.from_mapping({'DEBUG':True})

    参数为一个字典。

    四、Flask的路由系统

    在Flask中,路由是通过装饰器加到每个视图函数的:

    from flask import Flask, request, render_template
    
    app = Flask(__name__)
    
    
    @app.route('/index', methods=['GET', 'POST'])
    def hello_world():
        if request.method == 'GET':
            return render_template('index.html')
    
    
    if __name__ == '__main__':
        app.run()

    1.@app.route中的endpoint

    @app.route中的endpoint参数,就相当于django中的name参数,用来反向生成URL。

    from flask import Flask, request, render_template, url_for
    
    app = Flask(__name__)
    
    
    @app.route('/index', methods=['GET', 'POST'], endpoint='n1')
    def hello_world():
        print(url_for('n1'))  # 打印/index
        if request.method == 'GET':
            return render_template('index.html')
    
    
    if __name__ == '__main__':
        app.run()

    其中的url_for相当于django中的reverse函数,用来利用endpoint反向生成url。

    注意:如果我们不指定endpoint,则endpoint默认等于视图函数名,这里即"hello_world"。url_for("hello_world")即为"/index"。

    2.动态路由

    Flask的路由中,我们可以使用动态参数。

    from flask import Flask, request, render_template
    
    app = Flask(__name__)
    
    
    @app.route('/index/<int:nid>', methods=['GET', 'POST'])
    def hello_world(nid):
        print(nid)
        if request.method == 'GET':
            return render_template('index.html')
    
    
    if __name__ == '__main__':
        app.run()

    Flask是django使用动态路由的方式不同,在django中,动态路由是这样的:'/index/(?P<nid>d+)'。

    注意:<int:nid>中,int表示动态参数的类型,我们的视图函数接收的参数nid为整形,如果不写,则默认为字符串。

    动态参数的类型除了有int以外,还有以下几种类型:

    @app.route('/user/<username>')  # 字符串
    @app.route('/post/<int:post_id>')  # 整数
    @app.route('/post/<float:post_id>')  # 浮点数
    @app.route('/post/<path:path>')  # 路径类型
    @app.route('/post/<uuid:uuid>')  # UUID

    在使用动态路由的时候,也可以使用方向生成url:

    url_for("hello_world",nid=777)  # 生成/index/777

    五、Flask的请求数据

    在Flask中,视图函数没有request参数。Flask中的request是直接导入使用的(上下文管理)。

    from flask import Flask, request

    在视图函数中,可以直接使用request:

    #例如请求: http://127.0.0.1:5000/index/2019-03?name=leo
    #以下request的属性值为:
    request.method  # 请求类型
    request.args  # GET请求的数据
    request.args.get('user')
    request.form  # POST请求的数据
    request.form.get('user')
    request.values
    request.cookies  # Cookie数据
    request.headers
    """
    Accept: text/html, application/xhtml+xml, image/jxr, */*
    ...
    Connection: Keep-Alive
    """
    request.path  # 请求的url "/index/2019-12"
    request.full_path  # "/index/2019-12?name=leo" 包含query
    request.script_root  # ""
    request.url  # "http://127.0.0.1:5000/index/2019-12?name=leo"
    request.base_url  # "http://127.0.0.1:5000/index/2019-12"
    request.url_root  #"http://127.0.0.1:5000/"
    request.host_url  # "http://127.0.0.1:5000/"
    request.host  # "127.0.0.1:5000"
    request.files
    obj = request.files['file_name']
    obj.save('/var/www/uploads/' + secure_filename(f.filename))  #上传文件

    六、Flask的响应

    1.Flask的几种常用响应

    在Flask中,常用的响应方式有以下几种:

    1)响应字符串

    return "HelloWorld"  # 相当于django中的HttpResponse

    2)响应json数据

    from flask import jsonify
    return jsonify({'k':'v'})
    # 或 return json.dumps({'k':'v'})

    3)响应渲染模板

    from flask import render_template
    return render_template('login.html')  # 相当于django中的render

    4)响应重定向

    from flask import redirect
    return redirect('/index')

    2.设置响应头

    在Flask中如何设置响应头,特别是响应字符串的时候,无法设置响应头。

    from flask import make_response
    
    # 使用响应对象来封装要返回的字符串
    obj=make_response("hello world")
    # obj=make_response(render_template('index.html'))
    
    # 设置响应头的字段xxx
    obj.headers['xxx']='123'
    # 设置cookie
    obj.set_cookie('key','value')
    
    # 返回obj
    return obj

    七、jinjia2模板语言

    1.循环与字典取值

    Jinjia2模板语言和django自带的模板语言很相似,但功能更加强大。语法比较接近于Python。

    视图函数代码:

    from flask import Flask, request, render_template
    
    app = Flask(__name__)
    
    USER_INFO = {
        1: {'name': 'Leo', 'age': 32, 'gender': 'male'},
        2: {'name': 'Jane', 'age': 33, 'gender': 'female'},
        3: {'name': 'Jake', 'age': 12, 'gender': 'male'},
        4: {'name': 'Alex', 'age': 45, 'gender': 'male'},
        5: {'name': 'Lilei', 'age': 77, 'gender': 'female'}
    }
    
    
    @app.route('/users', methods=['GET', 'POST'])
    def user_list():
        if request.method == 'GET':
            return render_template('users.html', user_list=USER_INFO)
    
    
    if __name__ == '__main__':
        app.run()

    我们创建了一个字典,并返回给模板进行渲染。

    html模板代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Users</title>
    </head>
    <body>
    <h2>用户列表</h2>
    <table>
        <thead>
        <tr>
            <th>ID</th>
            <th>名字</th>
            <th>岁数</th>
            <th>性别</th>
        </tr>
        </thead>
        <tbody>
        {% for k,v in user_list.items() %}
            <tr>
                <td>{{ k }}</td>
                <td>{{ v.name }}</td>
                <td>{{ v.age }}</td>
                <td>{{ v.gender }}</td>
            </tr>
        {% endfor %}
        </tbody>
    </table>
    </body>
    </html>

    可以看到,使用for循环的方法和django模板语言是一样的。

    注意,user_list.items()这个括号一定要有,这和Python一样。

    在取值的时候,使用{{ v.name }}这种形式可以获取字典中字段的值。在Jinjia2中,还可以使用{{ v.get('name','默认') }}以及{{ v['name'] }}的形式来取值,和Python基本一致。

    2.数组取值

    除了1.中所述的用法,这里补充一些用法:

    {{ list.0 }}  # django模板语言风格取index为0的元素
    {{ list[0] }}  # python风格取index为0的元素

    列表按索引取元素,两种风格都支持。

    2)XSS问题

    当数据为html标签时:

    # data = "<input type='text' />"
    {{ data }}  # 显示字符串<input type='text' />
    {{ data|safe }}  # 显示input标签元素

    Flask默认帮我们做了XSS,要返回标签格式的字符串,在模板语言中使用"|safe"。

    当然,也可以在后端python中使用Markup来传递安全的标签字符串(类似django中的mark_safe):

    from flask import Markup
    
    Markup("<input type='text' />")  # 相当于django中的mark_safe

    3.传递函数引用并调用

    当我们在视图函数中传递给模板的参数是一个函数引用时:

    例如:

    def func(arg):
        return arg**2
    
    # 视图函数index
    def index():
        return render_template("index.html",func=func)  #将func函数引用传递到模板

    Jinjia2模板语言调用func:

    {{ func(6) }}  # 显示36

    虽然Jinjia2可以方便的调用函数(比django模板语言方便)。但是如果有多个页面都要调用这个函数,则每个对应的视图函数都要将该函数的引用传递给模板。则可以使用全局的模板函数(类似django中定义模板函数)。

    4.全局模板函数

    1)@app.template_global()装饰器

    使用@app.template_global()装饰器,可以将自定义函数变为全局的模板函数,直接可以在模板语言中调用。

    @app.template_global()
    def my_add(a,b)
        return a+b

    在模板中调用:

    {{ my_add(1,9) }}  # 显示10

    2)@app.template_filter()装饰器

    还有另一种装饰器@app.template_filter(),和template_global差不多,但是在模板语言中的调用方式不同:

    {{ 1|my_add(9) }}  # 显示10

    这两种方式在Jinjia2中其实效果差不多。

    我们可以这样理解

      @app.template_global装饰的模板函数,对应的是django中 @register.simple_tag装饰的模板函数(但由于django模板语言在调用模板函数时,不是使用括号来包裹参数,所以无法作为if的条件)。

      @app.template_filter装饰的模板函数,对应的是django中 @register.filter装饰的模板函数(形式差不多)

    由于Jinjia2中传参的方式和python很像,所以两种方式都可以作为if的条件。如下:

    # tempalte_global装饰的my_add
    {% if my_add(1,9) %}
    {% endif %}
    
    # tempalte_filter装饰的my_add
    {% if 1|my_add(9) %}
    {% endif %}

    5.定义宏(函数)

    Jinjia2模板语言支持定义宏(其实就是定义函数):

    <!-- 使用macro定义一个宏(相当于python的def定义函数) -->
    {% macro my_func(name,type='text',value='') %}
        <input type="{{ type }}" name="{{ name }}" value="{{ value }}"/>
    {% endmacro %}
    <!-- macro定义的函数,默认是不执行的,需要手动调用 --> {{ my_func('n1') }} {{ my_func("n2",type="button",value='确定') }}

    也就是说Jinjia2不仅可以支持在后台Flask中定义全局函数,还可以使用macro直接在模板中定义函数。

    八、使用装饰器给视图函数添加验证功能

    1.简单装饰器的问题

    # 最简单的装饰器
    def auth(func):
        def inner(*args, **kwargs):
            ret = func(*args, **kwargs)
            return ret
        return inner
    
    
    # index函数使用了auth装饰器
    @auth
    def index():
        print('index')
    
    # index2函数没有使用auth装饰器
    def index2():
        print('index2')
    
    
    if __name__ == '__main__':
        print(index.__name__)  # 打印inner
        print(index2.__name__)  # 打印index2

    可以看到,当一个函数使用了简单装饰器后,打印其函数名,编程了装饰器中的inner。

    在这种情况下,会影响我们使用反向生成url的功能,即url_for(视图函数名)。

    2.使用functools.wraps

    # 导入functools中的wraps
    from functools import wraps
    
    
    def auth(func):
        # 使用wraps装饰器
        @wraps(func)
        def inner(*args, **kwargs):
            ret = func(*args, **kwargs)
            return ret
    
        return inner
    
    
    @auth
    def index():
        print('index')
    
    
    if __name__ == '__main__':
        print(index.__name__)  # 打印index

    使用了functools的wraps装饰器,实际上运行index函数时,还是调用的是inner,但是wraps帮我们将index函数的元信息封装到了inner函数中。

    3.使用auth装饰器实现登录验证

    from flask import Flask, request, render_template, redirect, session, url_for
    
    app = Flask(__name__)
    # 使用密码盐(session默认保存在加密cookie中,必须设置密码盐)
    app.secret_key = 'snjdkfnjsk'
    
    USER_INFO = {
        1: {'name': 'Leo', 'age': 32, 'gender': 'male'},
        2: {'name': 'Jane', 'age': 33, 'gender': 'female'},
        3: {'name': 'Jake', 'age': 12, 'gender': 'male'},
        4: {'name': 'Alex', 'age': 45, 'gender': 'male'},
        5: {'name': 'Lilei', 'age': 77, 'gender': 'female'}
    }
    
    from functools import wraps
    
    
    # 认证用户是否登录
    def auth(func):
        @wraps(func)
        def inner(*args, **kwargs):
            if not session.get('user'):
                return redirect(url_for('login'))
            ret = func(*args, **kwargs)
            return ret
    
        return inner
    
    
    # 登录页面
    @app.route('/login', methods=['GET', 'POST'])
    def login():
        if request.method == 'GET':
            return render_template('login.html')
        user = request.form.get('username')
        pwd = request.form.get('password')
        # 用户名密码正确,则将用户名写到session中(默认保存在加密cookie中,默认cookie过期时间为31天)
        if user == 'leokale' and pwd == '123456':
            session['user'] = user
            # 登录成功,跳转到users页面
            return redirect('/users')
        # 登录失败,返回login页面,并显示用户名或密码错误
        return render_template('login.html', error='用户名或密码错误')
    
    
    # users页面,使用auth装饰器进行登录验证
    @app.route('/users', methods=['GET', 'POST'])
    @auth
    def user_list():
        if request.method == 'GET':
            return render_template('users.html', user_list=USER_INFO)
    
    
    if __name__ == '__main__':
        app.run()

    1)实现验证装饰器,必须使用functools.wraps装饰器,保证视图函数名不变

    2)在auth装饰器中实现验证登录功能(检查session中user是否存在)

    3)将auth装饰器应用到需要验证的页面

  • 相关阅读:
    Web Components 是什么
    HAL_RTC_MspInit Msp指代什么?
    C 枚举 相同的值
    EntityFramework Core并发深挖详解,一纸长文,你准备好看完了吗?
    ASP.NET Core MVC之ViewComponents(视图组件)知多少?
    .NET Core 1.1日期解析无APi、SQL Server数据转换JSON
    SQL Server-字字珠玑,一纸详文,完全理解SERIALIZABLE最高隔离级别(基础系列收尾篇)
    SQL Server-聚焦NOLOCK、UPDLOCK、HOLDLOCK、READPAST你弄懂多少?(三十四)
    SQL Server-聚焦深入理解死锁以及避免死锁建议(三十三)
    ASP.NET Core MVC上传、导入、导出知多少
  • 原文地址:https://www.cnblogs.com/leokale-zz/p/12214656.html
Copyright © 2020-2023  润新知