1、钩子函数介绍
在Flask中钩子函数是使用特定的装饰器装饰的函数。
为什么叫做钩子函数呢,是因为钩子函数可以在正常执行的代码中,插入一段自己想要执行的代码。那么这种函数就叫做钩子函数。
在客户端和服务器交互的过程中,有些准备工作或扫尾工作需要处理,比如:
- 在请求开始时,建立数据库连接;
- 在请求开始时,根据需求进行权限校验;
- 在请求结束时,指定数据的交互格式;
为了让每个视图函数避免编写重复功能的代码,Flask提供了通用设施的功能,即请求钩子。
2、before_first_request
在处理项目第一个请求前执行。
@app.before_first_request
def first_request():
print('first time request')
示例代码:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
print('执行index函数!')
return 'Hello world!'
@app.before_first_request
def before_first_request():
print("开始启动程序")
if __name__ == '__main__':
app.run(debug=True)
运行结果:
3、before_request
- 在每次请求前执行。通常可以用这个装饰器来给视图函数增加一些变量。
- 请求已经到达了Flask,但是还没有进入到具体的视图函数之前调用。
- 如果在某修饰的函数中返回了一个响应,视图函数将不再被调用。
- 一般这个就是在视图函数之前,我们可以把一些后面需要用到的数据先处理好,方便视图函数使用。
@app.before_request
def before_request():
if not hasattr(g,'glo1'):
setattr(g,'glo1','想要设置的')
示例代码1:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
print('执行index函数!')
return 'Hello world!'
@app.before_request
def before_request():
print("开始执行API!")
if __name__ == '__main__':
app.run(debug=True)
运行结果:
示例代码2:
from flask import Flask, g, session
app = Flask(__name__)
app.config['SECRET_KEY'] = 'absdfbdssdf'
@app.route('/')
def index():
print('执行index函数!')
return 'Hello world!'
@app.route('/login/')
def login():
print('开始登录')
session['name'] = 'dgw'
return '登录成功!'
@app.route('/home/')
def home():
print('进入个人中心')
if hasattr(g, 'name'):
return f'用户已经登录,用户名为:{g.name}'
else:
return '用户没有登录!'
@app.before_request
def before_request():
print("开始执行API!")
name = session.get('name')
if name:
g.name = name
print('这个是每次请求时,需要执行的逻辑!!!')
if __name__ == '__main__':
app.run(debug=True)
运行结果:
4、after_request
- 如果没有抛出错误,在每次请求后执行
- 接受一个参数:视图函数作出的响应
- 在此函数中可以对响应值在返回之前做最后一步修改处理
- 需要将参数中的响应在此参数中进行返回
示例代码:
from flask import Flask, g, session
app = Flask(__name__)
@app.route('/')
def index():
print('执行index函数!')
return 'Hello world!'
# 在执行完视图函数之后会调用,并且会把视图函数所生成的响应传入,可以在此方法中对响应做最后一步统一的处理
@app.after_request
def after_request(response):
print('本次接口执行完毕!')
response.headers["Content-Type"] = "application/json"
return response
if __name__ == '__main__':
app.run(debug=True)
运行结果:
5、teardown_request
- 在每次请求后执行
- 接受一个参数:错误信息,如果有相关错误抛出
示例代码:
from flask import Flask, g, session
app = Flask(__name__)
@app.route('/')
def index():
print('执行index函数!')
return 'Hello world!'
# 每一次请求之后都会调用,会接受一个参数,参数是服务器出现的错误信息
@app.teardown_request
def teardown_request(response):
print("teardown_request")
if __name__ == '__main__':
app.run(debug=True)
运行结果:
6、teardown_appcontext
不管是否有异常,注册的函数都会在每次请求之后执行。
@app.teardown_appcontext
def teardown(exc=None):
if exc is None:
db.session.commit()
else:
db.session.rollback()
db.session.remove()
7、template_filter
在使用Jinja2模板的时候自定义过滤器。
@app.template_filter("upper")
def upper_filter(s):
return s.upper()
详见博文中自定义过滤器:Flask中Jinja2模板过滤器_flask jinja,内置过滤器 attr-优快云博客
8、context_processor
上下文处理器。使用这个钩子函数,必须返回一个字典。这个字典中的值在所有模版中都可以使用。
使用场景:
这个钩子函数的功能是,如果一些在很多模版中都要用到的变量,那么就可以使用这个钩子函数来返回,而不用在每个视图函数中的 render_template 中去写,这样可以让代码更加简洁和好维护。
@app.context_processor
def context_processor():
if hasattr(g,'user'):
return {"current_user":g.user}
else:
return {}
示例代码:
from flask import Flask, request, g, session, current_app, render_template
import os
app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(24)
@app.route('/')
def index():
print('执行index函数!')
session['name'] = 'dgw'
return render_template('index.html')
@app.route('/home/')
def home():
print('进入个人中心')
if hasattr(g, 'name'):
print('name:', g.name)
return render_template('index.html')
@app.before_request
def before_request():
print('before_request!')
name = session.get('name')
if name:
g.name = name
@app.context_processor
def context_processor():
if hasattr(g, 'name'):
return {'current_user': g.name}
else:
return {}
if __name__ == '__main__':
app.run(debug=True)
# index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>这个是个人主页</h1>
<p>当前用户:{{ current_user }}</p>
</body>
</html>
运行结果:
9、errorhandler
errorhandler接收状态码,可以自定义返回这种状态码的响应的处理方法。在发生一些异常的时候,比如404错误,比如500错误,那么如果想要优雅的处理这些错误,就可以使用 errorhandler 来出来。
需要注意几点:
- 在errorhandler装饰的钩子函数下,记得要返回相应的状态码。
- 在errorhandler装饰的钩子函数中,必须要写一个参数,来接收错误的信息,如果没有参数,就会直接报错。
- 使用 flask.abort 可以手动的抛出相应的错误,比如开发者在发现参数不正确的时候可以自己手动的抛出一个400错误。
示例代码1:
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
print('执行index函数!')
1 / 0
return render_template('index.html')
@app.errorhandler(ZeroDivisionError)
def server_error(error):
return render_template('500.html')
if __name__ == '__main__':
app.run(debug=True)
运行结果:
示例代码2:
from flask import Flask, render_template, abort
app = Flask(__name__)
@app.route('/')
def index():
print('执行index函数!')
abort(500)
return render_template('index.html')
@app.errorhandler(500)
def server_error(error):
return render_template('500.html')
if __name__ == '__main__':
app.run(debug=True)
运行结果:
示例代码3: 【状态码虽然可以不写,但是推荐写上,这样可以告诉服务器是哪个错误】
from flask import Flask, render_template, abort
app = Flask(__name__)
@app.route('/')
def index():
print('执行index函数!')
abort(500)
return render_template('index.html')
@app.errorhandler(500)
def server_error(error):
return render_template('500.html'), 500 # 状态码虽然可以不写,但是推荐写上,这样可以告诉服务器是哪个错误
if __name__ == '__main__':
app.run(debug=True)
运行结果:
更多用法详见博文:Flask之异常处理_flask异常处理方法-优快云博客
10、综合案例使用
示例代码:
from flask import Flask
app = Flask(__name__)
# 在第一次请求之前调用,可以在此方法内部做一些初始化操作
@app.before_first_request
def before_first_request():
print("before_first_request")
# 在每一次请求之前调用,这时候已经有请求了,可能在这个方法里面做请求的校验
# 如果请求的校验不成功,可以直接在此方法中进行响应,直接return之后那么就不会执行视图函数
@app.before_request
def before_request():
print("before_request")
# if 请求不符合条件:
# return "laowang"
# 在执行完视图函数之后会调用,并且会把视图函数所生成的响应传入,可以在此方法中对响应做最后一步统一的处理
@app.after_request
def after_request(response):
print("after_request")
response.headers["Content-Type"] = "application/json"
return response
# 请每一次请求之后都会调用,会接受一个参数,参数是服务器出现的错误信息
@app.teardown_request
def teardown_request(response):
print("teardown_request")
@app.route('/')
def index():
return 'index'
if __name__ == '__main__':
app.run(debug=True)
运行结果:
- 在第1次请求时的打印:
before_first_request
before_request
after_request
teardown_request
- 在第2次请求时的打印:
before_request
after_request
teardown_request