Flask之钩子函数

发布时间 2023-06-04 21:31:41作者: 浩浩学习

Flask之钩子函数

类似django的中间件,作用就是在进入框架的之后 http方法之前或返回response之前进行一些操作 Flask的钩子函数可在注册时根据注册的app或者蓝图从而确定钩子函数作用的范围(可全局 也可作用某一个蓝图)

钩子函数

钩子函数可以分为两层说明,第一层是 app 层,第二层则是 blueprint 层

app 层

app 层的钩子函数有 before_request,before_first_request,after_request,teardown_request,下面我们一一分析。

after_request 和 teardown_request 的区别

两者最大的区别是在于,从Flask 0.7开始,如果出现未处理的异常,after_request 将不会被执行,而后者将正常运行并接收异常,其次还有两者的执行顺序,让我们通过代码去了解一下.

from flask import Flask, g, request

app = Flask(__name__)

@app.before_request
def before_request():
    print('before request started, %s' % request.url)

@app.before_first_request
def before_request():
    print('before first request started, %s' % request.url)

@app.after_request
def after_request(reponse):
    print("after request started, %s" % request.url)
    return reponse

@app.teardown_request
def teardown_request(exception):
    print("teardown request,%s,%s" % (exception,request.url))

@app.route('/')
def index():
    return 'Hello'

if __name__ == '__main__':
    app.run(debug=True)

结果如下:

结果很清晰,after_request 先执行(注意这个图和代码)

blueprint 层

blueprint 层关于request 的钩子函数其实和app层基本一致,有两点区别:

  1. 与 app 层相比,blueprint 层少了一个 before_first_request 的钩子函数
  2. 多了一些,可以在蓝图调用 app 层的钩子函数:before_app_first_request,before_app_request,after_app_request,after_app_request,teardown_app_request

请求上下文(request context)

为什么这里会突然出现这个标题呢?我们在哪里用到了这个吗?还记得上个图吗,我们在所有的请求中都访问了request对象,并且成功了输出了 url,

但是我们并没有传入它,它是一个全局对象吗?让我们测试一下:

from flask import Flask, g, request

app = Flask(__name__)

def test_request():
    print("test request,%s" % request.url)

test_request()

@app.route('/')
def index():
    return 'Hello'

if __name__ == '__main__':
    app.run(debug=True)

看一下截图:

由此可见 request 对象仅可以在请求上下文的生命周期可以访问,由此不难得出,我们上面说到几个钩子函数也是挂载到请求上下文的生命周期的不同阶段从而发挥作用的。

方法:

一共四种方方法

before_first_request()

执行时间:在处理第一个请求前运行 before_request之前

格式:

@App.before_first_request
def before_app_first_request():
    print('只有在处理第一次请求之前执行')

参数:

​ 没有参数

返回值:

​ None 请求继续

​ response对象 终止本次请求 直接返回结果

示例:

注意:和 before_request 不同的是, 它的非空返回值会被忽略。

问题来了:before_first_request 和 before_request 加载顺序是什么样子呢?

让我们通过下面的代码看一下:

from flask import Flask, g, request

app = Flask(__name__)

@app.before_request
def before_request():
    print('before request started')

@app.before_first_request
def before_request():
    print('before first request started')

@app.route('/')
def index():
    return 'Hello'

if __name__ == '__main__':
    app.run(debug=True)

结果如图:这次 first_request 胜出了。

 

before_request()

执行时间:每次请求都执行 http方法之前

格式:

@App.before_request
def before_app_request():
    print('在视图函数执行之前执行')

参数:

​ 没有参数

返回值:

​ None 请求继续

​ response对象 终止本次请求 直接返回结果

示例:

from flask import Flask,

app = Flask(__name__)

@app.before_request
def before_request():
    print('before request started')
    return "example01" 

@app.route('/')
def index():
    return 'Hello world'' 

if __name__ == '__main__':
    app.run(debug=True)

这个例子下当访问 localhost:5000/的时候,前端渲染的值为 “example01” 而不是 “Hello world”

 

after_request()

执行时间:如果没有未处理的异常抛出,在每次请求后运行(http方法之后执行)

格式:

@App.after_request
def after_request(response):
    pass
    return response

参数:

​ 视图方法中返回的response对象

返回值:

​ response对象 可在返回前修改

 

teardown_request()

执行时间:在每次请求后运行,即使有未处理的异常抛出也执行

格式:

# 请每一次请求之后都会调用,会接受一个参数,参数是服务器出现的错误信息
@app.teardown_request
def teardown_request(e):
    print("teardown_request")

参数:

​ exception错误信息

返回值:

​ None

示例:仿照Django中的中间件

第一步

应用目录下创建middlewares包目录,每个功能实现以个中间件(每个功能是一个py文件)

第二步

在middlewares目录下创建中间件文件

from flask import request
from flask import session
from flask import g

#参数:蓝图对象
def verifycodeMiddleware(blueprint):
    @blueprint.before_request
    def before():
        print("------验证验证码-------")

第三步

注册中间件(蓝图文件中)

# 注册中间件
from myApp.middlewares import verifycodeMiddleware
verifycodeMiddleware(myApp)

扩展:

from flask import request
from flask import session
from flask import g

#参数:蓝图对象
def verifycodeMiddleware(app):
    @app.before_request
    def before():
        print("****验证验证码****")
app.py -》注册 verifycodeMiddleware(app)

说明

# 钩子函数
@myApp.before_first_request
def first():
    print("-------------first")
@myApp.before_request
def before():
    print("-------------before")
    # 验证验证
    # 获取前端发送的验证码
    # 获取session中的验证码
    # 判断两者是否相同,相同返回None,否则重定向会登陆界面

    # 验证是否登录
    # 获取状态保持相关信息(账号)
    # 如果没有状态保持说明没有登陆,重定向登陆界面
    # 根据状态保持的账号获取用户对象
    # 获取客户端发送的cookie中的键为token的值
    # 如果没有说明没有登陆,重定向登陆界面
    # 判断用户中的token与cookie中的token值是否相同,不相同则重定向登陆界面
@myApp.after_request
def after(response):
    print("-------------after")
    return response

@myApp.teardown_request
def teardown(exception):
    print("-------------teardown")
    print(exception)

什么是g

from flask import g

flask,g对象是专门用来存储用户数据的,它是global的缩写,g是全局变量,在整个request生命周期内生效。

g对象如何使用

1.官方解释

The application context is created and destroyed as necessary. It never moves between threads and it will not be shared between requests.
As such it is the perfect place to store database connection information and other things. The internal stack object is called flask.appctx_stack.
Extensions are free to store additional information on the topmost level, assuming they pick a sufficiently unique name and
should put their information there, instead of on the flask.g object which is reserved for user code.

2.非官方解释
g 保存的是当前请求的全局变量,不同的请求会有不同的全局变量,通过不同的thread id区别,像数据库配置这样重要的信息挂载在app对象上,一些用户相关的数据,就可以挂载在g对象上,这样就不需要在函数里一层层传递。

3.使用案例

 

from flask import Flask, request, g


app = Flask(__name__)


@app.route('/youhui')
def youhui():
    grade = request.args['grade']
    g.grade = grade
    return get_amount_by_grade()


def get_amount_by_grade():
    grade = g.grade
    if grade == 'a':
        return '100'
    else:
        return '80'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5500)

g对象的出现,让你在任何位置都能获得用户数据,避免了在函数参数里传递这些数据。

g对象的生命周期

g对象在整个request请求处理期间生效,这表明,g对象是与request是一一对应的。一次request请求,就有一个g对象,在这次请求之前,之后,以及同时间的请求里,他们互不干扰。

你在g对象里存储的数据,只能在这一次请求里使用,请求处理结束后,这个g对象就销毁了,存储的数据也就不见了。

g对象的生命周期虽然只是一次请求的生命周期,但它是一个应用 上下文对象

g对象和session的区别(非官方)

session对象是可以跨request的,只要session还未失效,不同的request的请求会获取到同一个session,但是g对象不是,g对象不需要管过期时间,请求一次就g对象就改变了一次,或者重新赋值了一次。

也就是说session可以在我们的这个网站随意都可以用 而 g只能是这次的请求如果重定向之后就会改变。

感谢分享:

https://blog.csdn.net/weixin_44795429/article/details/115279400
https://blog.csdn.net/General_zy/article/details/122265428