我们在写项目的时候往往会有一些全局性的需求,比如在应用启动之初初始化一些数据,建立数据库链接等。对发起的所有请求记录一些数据,对某些请求进行一些拦截。在请求结束的时候自定义响应数据等。
请求钩子
针对这些全局性的需求,flask框架提供了请求钩子,所谓请求钩子就是可以挂在请求和响应的不同阶段,进行植入,拦截一些操作。请求钩子以装饰器的形式作用到知道一点的函数上,被作用的函数内部可以自定义具体的操作。根据被装饰的请求钩子,这些被装饰的函数会在不同阶段被触发。下面介绍一下flask框架常用的三个请求钩子:
before_first_request:
在处理第一个请求前执行
before_request:
在每次请求前执行,如果在被装饰的函数中返回了一个响应,视图函数将不再被执行。
after_request:
如果没有抛出错误,在每次请求后执行
接受一个参数:此参数是视图函数作出的响应
在此函数中可以对响应值在返回之前做最后一步修改处理
需要将参数中的响应在这个被装饰的函数中进行返回
请求钩子执行先后顺序
我们下面同时定义一个视图函数index和三个被装饰的函数,分别在他们内部进行打印,然后看下他们的执行顺序。
@app.route('/')
def index():
print('index')
return 'Ok'
@app.before_first_request
def first():
print('first')
@app.before_request
def every():
print('every')
@app.after_request
def after(response):
print('after')
return response
启动之后请求index视图函数,在控制台可以看到执行顺序如下:
first
every
index
after
由此看出请求钩子的执行顺序为before_first_request->before_request->视图函数index->after_request
再次请求index视图函数,这次的在控制台看到的结果如下:
every
index
after
这次我们可以看到除了before_first_request这个钩子函数不被执行之外,别的顺序都没有变化。
也就验证了我们之前的说明,before_first_request钩子只在第一个请求前执行,befor_request在每次请求都执行,且在视图函数之前执行,after_request在视图函数执行之后执行,且要返回由视图函数传递过来的响应。
before_request的拦截作用
下面我们把before_request钩子作用的函数,改造成如下,看看会是什么结果。
@app.before_request
def every():
print('every')
return 'every' # 如果返回结果,请求就直接从这里返回了,而不再执行视图函数了。
再次访问index视图函数,发现浏览器显示every
字样。也同样验证了我们在上面的说明,如果before_request钩子作用的函数有返回值,就在此返回,不再执行视图函数了。
统计网站浏览次数
基于以上特性,我们可以利用before_first_request、before_request和session技术,来统计网站浏览次数。思路如下:
在before_first_request初始化变量并写入session,由于此钩子只在应用之初执行一次,所以不会重复初始化变量。然后,由于每次对网站的请求都会执行befor_request钩子,所以我们在其内部取到保存在session中的变量,并且每请求一次就累计1,这样就能起到统计网站整体的浏览次数的目的,我们可以在任意视图函数中从session中取到总的浏览次数,并显示出来。代码如下:
@app.route('/')
def index():
return '总浏览量:' + str(session['sum']) # 从session中取出总次数并打印
@app.before_first_request
def set_sum():
session['sum'] = 0 # 初始化变量,并保存到session当中
@app.before_request
def add_sum():
session['sum'] += 1 # 从session中取出总次数,每次请求都累计浏览总次数
运行以上代码,访问index视图函数,可以看到每次刷新页面,总次数都会增加,有了上面的额思路分析和代码注释我想大家应该能清晰透彻的理解这段代码的含义吧。
记录最后访问网站时间
下面我们利用after_request钩子能接收视图函数传递来的响应这一特性,在响应中将用户最后访问网站的时间记录到cookie里面。然后就可以在视图函数中,通过cookie拿到用户最后的访问时间,然后展示出来。这里要用到时间获取包,和转换时间格式的函数。记得引入datetime包。
# 引入datetime包
from datetime import datetime
@app.route('/')
def index():
print('index')
return '最后访问时间:' + str(request.cookies.get('time'))
@app.after_request
def record_login_time(response):
response.set_cookie('time', str(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
return response
启动服务之后,访问index视图函数,每次刷新即会显示最后访问时间。
限制指定IP访问
下面结合before_request钩子的拦截特性,可以实现这样的需求,可以在before_request请求钩子内部判断客户端的地址,进行访问控制,当然我们如果是在本地测试,那么本地的客户端地址就是127.0.0.1所以我们可以来判断是否为此ip地址,如果是,就可以进行拦截,这样的话,当我们再请求index视图函数的时候,就已经不能访问了,被拦截在了befor_request钩子当中了。
@app.route('/')
def index():
return 'index'
@app.before_request
def add_sum():
ip = request.__dict__['environ']['REMOTE_ADDR']
if ip == '127.0.0.1':
return '不允许访问!'
验证一下,是不是已经不能访问index视图函数了?浏览器显示不允许访问
字样即是验证成功。
异常捕获
下面再介绍一下异常捕获装饰器。一般我们在访问网站的时候,如果访问了一个不存在的URL,那么网站就会提示我们404错误,并告诉我们这个页面不存在,那么这个404页面在一个网站中是同一个页面,访问所有不存在的页面都会跳到此页面,那么这个是怎么做到的呢?下面我们同样可以用装饰器来实现。
@app.errorhandler(404) # 传递需要捕获的状态码
def error(e): # 将错误传递到被装饰的函数内部
print(e)
return '不存在的页面' # 自定义的显示内容
此装饰器接收一个参数:http状态码。即表示我们要捕获的错误状态码,同时要把错误异常传递到被装饰的函数内部,此函数的返回结果即为我们在浏览器显示的内容。这时如果我们访问一个不存在的页面就会看到:不存在的页面
。