flask有一批特殊的装饰器,比如brefore_request, brefore_first_request,after_request, teardown_request这些装饰器提供了非常灵活的功能,可以在请求处理请求前后做一些重要的操作,假如没有这些装饰器,很多功能实现起来就很不方便。
被brefore_request装饰的函数会在处理路由规则对应的视图函数之前执行,相当于在所有请求开始之前做了一道拦截,你可以过滤掉那些你不希望处理的请求,或者是做一些特别操作。
被brefore_request装饰的函数的返回值如果是None,那么这个请求就会被后面的视图函数所处理,如果不是None,则等于提前做了处理,返回给客户端,后面的视图函数则接收不到这一次请求。
你可能编写了多个被brefore_request装饰的函数,在执行时,按照这些函数注册的顺序来依次执行,如果某个函数返回值不是None,则请求提前结束,后续的函数不会被执行。
下面的示例里,如果客户端的ip不是127.0.0.1, 就不允许他访问服务,利用before_request装饰器,你可以为web服务做IP过滤。
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def index():
return 'ok'
@app.before_request
def check_ip():
print(request.remote_addr)
if request.remote_addr != "127.0.0.1":
print('该IP不允许访问')
return 'ok'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5500)
有了brefore_request 做基础,理解brefore_first_request就容易了,被brefore_first_request装饰的函数,会在整个web服务第一次收到请求时调用,这意味着整个服务只会调用一次,而被before_request装饰的函数则在每次请求到来时都会调用。
brefore_first_request 适合做一些初始化的工作,比如建立数据库连接等任何你认为不需要在服务器时进行而在处理请求前又必须做好准备的操作。假设所有准备工作都在web启动时做,可能会让服务启动的很慢,分一部分工作到brefore_first_request可以加快服务启动速度。
当你自定义的视图函数正常执行结束后,就会调用被after_request装饰的函数,它可以在请求结束后做一些收尾工作,比如关闭数据库连接,记录一些日志。被after_reqeust装饰的函数执行顺序刚好与注册顺序相反,最先被装饰的函数最后执行。
@app.after_request
def log(response):
print('log')
return response
被after_request装饰的函数必须接收一个response对象,并且在函数结束后返回response对象,在函数里,你就可以对response对象进行修改。
flask-compress插件可以为flask返回的数据进行压缩处理,它的原理就是注册一个after_request函数,当请求结束后,数据还没有返回给客户端,flask-compress 获取到返回数据对数据进行压缩并修改headers里的content-length首部,最终实现了数据压缩。
teardown_request 也是在请求结束后被执行,执行顺序与注册顺序相反,先注册后执行。这样看来,它与after_request 功能是相同的,岂不是多余了。
实则不然,teardown_request的执行与请求上下文挂钩,只有在请求上下文被 pop 出请求栈的时候才会触发 teardown_request,而after_request被执行时,请求上下文还没有出栈,这是执行时机上的不同。
另外一个不同,才是本质的不同,被teardown_request装饰的函数必须接收一个exception参数,如果在请求处理过程中发生异常且没有被捕获,则传入异常对象,你可以在这里对异常进行记录,被teardown_request装饰的函数没有返回值,after_request则必须返回一个flask response对象。
@app.teardown_request
def tear(exception):
print(exception)
teardown_appcontext 是在应用上下文被弹出栈时被执行,teardown_request是在请求上下文被弹出栈是被执行,teardown_appcontext更加靠后一些,除此以外,他们几乎相同,被teardown_appcontext 装饰的函数必须接收一个execption参数,如果处理过程中有未捕获的异常,则传入该参数,我个人的理解,如果想在请求之后做一些清理收尾工作,使用teardown_request就足够了。
QQ交流群: 211426309