• flask——请求、响应、请求扩展、session、闪现、蓝图、g对象、flask-session


    系列文章目录

    第一章 介绍、安装、快速使用、配置文件、路由系统、视图

    第二章 请求、响应、请求扩展、session、闪现、蓝图、g对象、flask-session



    一、请求

    方法作用
    request.method请求方式
    request.argsget 请求参数
    request.formpost提交的数据
    request.valuesget,post提交的数据总和
    request.cookiescookies
    request.headers请求头
    request.path路由路径,例如: /login
    request.full_path路由路径以及路径中携带的参数,例如: /login?name=kdq&age=88
    request.script_root当前url路径的上一级路径
    request.url完整的url,例如: http://127.0.0.1:5000/login?name=kdq&age=88
    request.base_url部分的url(不包括路径参数),例如: http://127.0.0.1:5000/login
    request.url_root当前url的路径的上一级完整路径(域名+端口),例如: http://127.0.0.1:5000/
    request.host_url域名+端口,例如: http://127.0.0.1:5000/
    request.host不带http的域名+端口,例如: 127.0.0.1:5000
    request.files.get(‘file’)获取文件

    二、响应

    1.make_response

    向浏览器中写入cookie,返回方法可以使用make_response包裹一下变成响应对象

    res=make_response(render_template('home.html'))
    res.set_cookie('name','bbc')
    
    • 1
    • 2

    2.向浏览器中写响应头

    res.headers['X-Something'] = 'A value'
    
    • 1

    三、请求扩展

    flask的请求扩展是使用装饰器,可以完成请求传入、请求传出、抛异常、资源找不到等等情况的,执行该装饰器装饰的函数。

    from flask import Flask, redirect, render_template, jsonify
    
    app = Flask(__name__)
    
    
    @app.route('/', methods=['GET'])
    def index():
    
        return render_template('home.html')
    
    
    @app.before_first_request  # 项目第一次运行时执行一次该函数(flask2.3已弃用该装饰器)
    def beforefr():
        print('before_first_request')  # 此处无法return返回响应
    
    
    @app.before_request  # 请求传入之前执行
    def beforer():
        print('before_request')
        # return 'asdd' 替换request并终止request的运行流程直接执行response操作
        # return render_template('home.html') 替换request并终止request的运行流程直接执行response操作
        # return redirect('/login') 替换request并终止request的运行流程直接执行response操作
        # return jsonify('name', 'ool') 替换request并终止request的运行流程直接执行response操作
        return None  # 继续执行request的运行流程
    
    
    @app.after_request  # 请求传入之后执行
    def afterr(response):  # 参数为请求之后返回的响应
        print('after_request')
        # return 'asdd' 替换request并终止request的运行流程直接执行response操作
        # return render_template('home.html') 替换request并终止request的运行流程直接执行response操作
        # return redirect('/login') 替换request并终止request的运行流程直接执行response操作
        # return jsonify('name', 'ool') 替换request并终止request的运行流程直接执行response操作
        return response  # 继续执行request的运行流程
    
    
    @app.teardown_request  # 无论程序是否出异常,都会执行 app.debug=False模式才行
    def teardownr(e):  # 参数为错误信息
        print('-------------')
        print(e)  # 此处无法return返回响应
        print('-------------')
    
    
    @app.errorhandler(500)  # 触发对应错误状态时执行(错误状态自行设置)
    def errorh(arg):
        print(arg)  # 对错误信息的处理
        return jsonify({'code': 888, 'msg': '请联系系统管理员'})
    
    
    @app.template_global()  # 标签
    def sum(a, b):
        return a + b
    
    
    @app.template_filter()  # 过滤器
    def db(a1, a2, a3):
        return (a1 - a2) * a3
    
    
    if __name__ == '__main__':
        app.run()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    过滤器和标签的使用:

    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Titletitle>
    head>
    <body>
    {{sum(99,1)}} 标签使用
    <hr>
    {{5|db(3,2)}} flask的模板语法中过滤器可以带n个参数第一个固定在|左侧
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    执行顺序:
    1 @app.before_first_request 项目启动时执行一次
    2 @app.before_request 请求传入之前执行所有的该装饰器(多个情况从代码上到下执行,如果有返回值直接跳到response执行流程)
    3 @app.after_request 请求结束,传递响应时执行(多个情况从代码上到下执行,如果有返回值直接将response返回给客户端)
    4 @app.errorhandler 监听异常(可进行异常处理、记日志等)
    5 @app.teardown_request 只能使用异常信息,无法对异常进行处理(无法使用return)


    四、session

    1.session源码分析

    通过app.session_interface

    找到了

    session_interface: SessionInterface = SecureCookieSessionInterface()
    
    • 1

    在SecureCookieSessionInterface类对象中找到了俩个方法
    其中open_session是读取session用的
    save_session是写入session用的

        def open_session(
            self, app: "Flask", request: "Request"
        ) -> t.Optional[SecureCookieSession]:
            s = self.get_signing_serializer(app)
            if s is None: # 判断secret_key是否为None
                return None
            val = request.cookies.get(self.get_cookie_name(app))
            if not val: # 判断session是否存在于请求中
                return self.session_class()
            max_age = int(app.permanent_session_lifetime.total_seconds()) # session过期时间默认为31天
            try:
                data = s.loads(val, max_age=max_age) 
                return self.session_class(data)
            except BadSignature:
                return self.session_class()
    
        def save_session(
            self, app: "Flask", session: SessionMixin, response: "Response"
        ) -> None:
            name = self.get_cookie_name(app)
            domain = self.get_cookie_domain(app)
            path = self.get_cookie_path(app)
            secure = self.get_cookie_secure(app)
            samesite = self.get_cookie_samesite(app)
            httponly = self.get_cookie_httponly(app)
    
            if not session: # session是否为空
                if session.modified: # 如果session发生改动
                    response.delete_cookie( # 删除session
                        name,
                        domain=domain,
                        path=path,
                        secure=secure,
                        samesite=samesite,
                        httponly=httponly,
                    )
    
                return
    
            if session.accessed:
                response.vary.add("Cookie")
    
            if not self.should_set_cookie(app, session):
                return
    
            expires = self.get_expiration_time(app, session) # session的生成
            val = self.get_signing_serializer(app).dumps(dict(session))  # type: ignore
            response.set_cookie( # 将session放入response中
                name,
                val,  # type: ignore
                expires=expires,
                httponly=httponly,
                domain=domain,
                path=path,
                secure=secure,
                samesite=samesite,
            )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    2.session的使用

    from flask import Flask, make_response, session, request
    
    app = Flask(__name__)
    app.debug = True
    app.secret_key = 'asd$%@#_sdasfrt'
    
    @app.route('/set_session')
    def set_session():
        # 生成session方式一 该方式存放的session是明文
        # res = make_response('session生成')
        # res.set_cookie('name', 'bbc')
        # return res
    
        # 生成session方式二 该方式需要配置secret_key
        session['name'] = 'bbc'
        return 'session生成'
    
    @app.route('/get_session')
    def get_session():
        # 获取session方式一
        # name = request.cookies.get('name')
        # return 'session,name:%s'%name
    
        # 获取session方式二
        name = session['name']
    
        # 删除session
        # session.pop('name')
        return 'session,name:%s' % name
    
    if __name__ == '__main__':
        app.run()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    五、闪现(flash)

    假设在a页面操作出错,跳转到b页面,在b页面显示a页面的错误信息

    本质其实就是在a页面,把信息放到了session,在b页面把信息取出来

    在一次请求中,把一些数据放在闪现中,下次请求就可以从闪现中取出来,取一次就没了(使用分类也是这样)

    不使用分类示例:

    from flask import Flask, flash, get_flashed_messages, session, render_template, redirect
    
    app = Flask(__name__)
    
    app.debug = True
    app.secret_key = 'asd#$)*)asd@#55'
    
    @app.route('/')
    def home():
        try:
            b = 5 / 0
        except:
            flash('主页出错了') 
            return redirect('/error')
        return render_template('home.html')
    
    @app.route('/error')
    def errorhtml():
        return render_template('error.html', error=get_flashed_messages())
    
    if __name__ == '__main__':
        app.run()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    使用分类示例:

    from flask import Flask, flash, get_flashed_messages, session, render_template, redirect
    
    app = Flask(__name__)
    
    app.debug = True
    app.secret_key = 'asd#$)*)asd@#55'
    
    @app.route('/')
    def home():
        try:
            b = 5 / 0
        except:
            flash('主页出错了', category='server')
            flash('计算出错了', category='msg')
        return render_template('home.html')
    
    @app.route('/errormsg')
    def errormsg():
        return render_template('error.html', error=get_flashed_messages(category_filter='msg'))
    
    @app.route('/errorserver')
    def errorserver():
        return render_template('error.html', error=get_flashed_messages(category_filter='server'))
    
    if __name__ == '__main__':
        app.run()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    六、蓝图(Blue_print)

    蓝图是对目录的划分,当我们的项目越来越大时,如果所有代码都放在一个py中会越来越臃肿,代码的可读性与维护性就会降低,这个时候划分目录可以让项目便于维护和开发。

    不使用蓝图进行目录划分:

    在这里插入图片描述

    home.html

    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Titletitle>
    head>
    <body>
    <h1>主页h1>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    login.html

    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Titletitle>
    head>
    <body>
    <h1>登录h1>
    <from>
        <p>用户名 <input type="text">p>
        <p>密码 <input type="password">p>
        <button type="submit">登录button>
    from>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    views/_ _ init _ _.py

    from flask import Flask, render_template
    
    app = Flask(__name__, template_folder='../templates')
    app.debug = True
    app.secret_key = '123%#jsd()%GGE34'
    
    from . import home
    from . import login
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    home.py

    from Blue_print.views import app, render_template
    
    @app.route('/')
    def home():
        return render_template('home.html')
    
    • 1
    • 2
    • 3
    • 4
    • 5

    login.py

    from Blue_print.views import app, render_template
    
    @app.route('/login')
    def login():
        return render_template('login.html')
    
    • 1
    • 2
    • 3
    • 4
    • 5

    app.py

    from Blue_print.views import app
    
    if __name__ == '__main__':
        app.run()
    
    • 1
    • 2
    • 3
    • 4

    不使用蓝图划分目录容易出现循环导入问题

    1.蓝图的使用步骤:

    1 实例化得到蓝图对象 ---可以传一些参数:account = Blueprint('account', __name__,templates,static)
    2 把蓝图对象在app中注册---》app.register_blueprint(account,制定前缀)
    3 使用蓝图对象,注册路由
    4 蓝图有自己的请求扩展--》app对象总的请求扩展,每个蓝图有自己的请求扩展
    
    • 1
    • 2
    • 3
    • 4

    使用蓝图来做目录划分 主要目的是使用蓝图对象,取代app对象

    2.小项目蓝图

    -小型项目:目录结构如下,只有一个app的项目
            -flask_pro               #项目名
                -pro_flask          # 文件夹
                    -__init__.py    # 包的init文件
                    -static          #静态文件
                    	-code.png
                    -templates       #模板文件
                    	-index.html
                    -views           #视图函数写在里面
                        -account.py    #订单相关视图函数
                        -user.py     #用户相关视图函数
                        -blog.py     #博客相关视图函数
                 -manage.py          # 项目的启动文件
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述

    blog.html

    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Titletitle>
    head>
    <body>
    <h1>blogh1>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    login.html

    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Titletitle>
    head>
    <body>
    <form action="">
      <p>用户名:<input type="text">p>
    form>
    <img src="/static/code.png" alt="">
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    account.py

    
    from flask import Blueprint
    from flask import render_template
    # 实例化得到蓝图对象
    account = Blueprint('account', __name__)
    
    
    @account.route('/login.html', methods=['GET', "POST"])
    def login():
        return render_template('login.html')
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    blog.py

    from flask import Blueprint, render_template,request
    
    blog = Blueprint('blog', __name__)
    
    @blog.route('/get_blog')
    def get_blog():
        print(request.path)
        return render_template('blog.html')
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    pro_flask/ _ _ init_ _.py

    
    from flask import Flask
    
    app = Flask(__name__,template_folder='templates',static_folder='statics',static_url_path='/static')
    
    from .views.account import account
    from .views.blog import blog
    
    # 注册3个蓝图,把他们注册进app中
    app.register_blueprint(account)
    app.register_blueprint(blog)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    manage.py 名字可以自定义

    from pro_flask import app
    
    
    if __name__ == '__main__':
        app.run()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3.大型项目蓝图

    pro_flask   # 项目名
        -pro_flask #包名
            -admin #admin  app的名字
                -static #app自己的静态文件
                -templates #app自己的模板文件
                -__init__.py #包的init
                -views.py   #app自己的视图函数
            -web  #web   app的名字
                -static  #app自己的静态文件
                -templates #app自己的模板文件
                -__init__.py #包的init
                -views.py  #app自己的视图函数
            -__init__.py
        -run.py # 启动文件
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述

    admin.html

    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Titletitle>
    head>
    <body>
    <h1>我是adminh1>
    <img src="/admin/static/mv.jpg" alt="">
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    admin/_ _ init_ _.py

    
    from flask import Blueprint
    
    # 第一步:初始化蓝图
    admin = Blueprint(
        'admin',
        __name__,
        template_folder='templates',  # 指定该蓝图对象自己的模板文件路径
        static_folder='static'       # 指定该蓝图对象自己的静态文件路径
    )
    
    
    from . import views
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    admin/views.py

    from . import admin
    from flask import render_template
    
    
    @admin.before_request
    def before():
        print('admin 的 before')
    
    
    # 第三步:使用蓝图注册路由
    @admin.route('/index')
    def index():
        return render_template('admin.html')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    web/_ _ init_ _.py

    
    from flask import Blueprint
    
    web = Blueprint(
        'web',
        __name__,
        template_folder='templates',
        static_folder='static'
    )
    from . import views
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    web/views.py

    from . import web
    
    @web.route('/index')
    def index():
        return 'Web.Index'
    
    • 1
    • 2
    • 3
    • 4
    • 5

    pro_flask/_ _ init_ _.py

    
    from flask import Flask
    
    
    from .admin import admin
    from .web import web
    
    app = Flask(__name__)
    app.debug = True
    
    # 第二步:在app中注册蓝图
    app.register_blueprint(admin, url_prefix='/admin') # url_prefix='/admin' 访问这个app的前缀,等同于include
    app.register_blueprint(web)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    run.py

    from pro_flask import app
    
    if __name__ == '__main__':
        app.run()
    
    • 1
    • 2
    • 3
    • 4

    七、g对象

    flask 中有个 g 对象,在一次请求中,可以赋值,取值 。当次请求中的全局对象,在一次请求中写入值,后续就可以取出来
    如果使用request存值取值会发生request中原有的值被覆盖掉,所以为了防止request中原有的值被覆盖掉,flask提供了g对象

    g和session的区别:
    g只对当次请求有效
    session对该用户的所有请求都有效

    g对象的使用方式:

    g.key = value # 存值
    g.name # 取值
    
    • 1
    • 2

    示例代码

    from flask import Flask, g, request,session
    
    app = Flask(__name__)
    app.debug=True
    
    
    @app.before_request
    def before():
        print(type(g))  # werkzeug.local.LocalProxy    代理模式
        print(type(session))
        print(type(request))
        if request.path == '/':
            # request.method='lqz'
            g.name = 'lqz'  # 放到g对象中
        else:
            # request.name = 'pyy'
            g.name = 'pyy'
            
    def add(a,b):
        print(g.name)
        # print('-----', request.name)
    
    @app.route('/')
    def index():
        print(g.name)
        # print('-----',request.name)
        # add(1,2)
        return 'hello'
    
    
    @app.route('/home')
    def home():
        print(g.name)
        return 'hello'
    
    
    if __name__ == '__main__':
        app.run(host='0.0.0.0', port=8080)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    八、flask-session

    flask的session默认以cookie的形式放到浏览中,我们想把session,放到服务端保存(redis、mysql等)

    只需要写一个类,编写open_session和save_session方法

    使用flask-session

    #使用flask-session:方式一:
    from flask_session import RedisSessionInterface
    app.session_interface=RedisSessionInterface(redis=None,key_prefix='luffy_') 
    # key_prefix配置key的前缀
    # redis配置redis
    
    • 1
    • 2
    • 3
    • 4
    • 5
    #方式二:通用方案
    from redis import Redis
    from flask_session import Session
    #### 以下三行会放在配置文件中
    app.config['SESSION_TYPE'] = 'redis' 
    app.config['SESSION_KEY_PREFIX'] = 'luffy' # 配置key的前缀
    app.config['SESSION_REDIS'] = Redis(host='127.0.0.1',port='6379')
    ####
    Session(app)  #常见这种写法
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • 相关阅读:
    数据结构:树和二叉树的概念及性质
    JMeter使用记录
    二、JavaScript
    JAVAEE进阶 -初识框架 - 细节狂魔
    uniapp 对于scroll-view滑动和页面滑动的联动处理
    extern的使用
    QT5自定义下拉框为QTreeView类型(树形分上下级)的下拉框(QComboBox)(超详细步骤)
    MyBatis中的ResultMap有什么作用
    Python算法例8 将整数A转换为B
    F检验临界值表(Frideman检验表)
  • 原文地址:https://blog.csdn.net/kdq18486588014/article/details/126235710