文章内容

2018/9/20 14:40:23,作 者: 黄兵

Flask框架钩子函数使用方式及应用场景分析

在正常执行的代码前中后,强行插入执行一段你想要实现的功能的代码,这种函数就叫做钩子函数。钩子函数就是等同于高速公路上的收费站,进高速之前给你一个卡,并检查你是否超重。离开之前收你,也可以拦住你安检一下。

一、基础概念:

  • request: Flask的请求上下文,包含请求变量如:method、args、form、values、endpoint、headers、remote_addr都是比较常用的。

  • session:Flask的请求上下文,用于存放用户的会话信息。

  • current_app:Flask的应用上下文,返回当前app的方法和属性,可以勉强理解为类全局变量。

二、七种钩子

第一个钩子:@app.before_first_request

只在第一次请求之前执行,也就是启动项目,不会执行,只会在第一次有人发起请求时,才会触发这个钩子中的代码。

全局场景:可以带动一个异步执行的函数,进行一些健康指标的检查,如果发现有异常,则截断后续的请求,将整个Flask应用停止。

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

第二个钩子:@app.before_request

这是最重要的一个钩子,在每次请求之前可以注入你要的逻辑的钩子。在app下的before_request,过滤的是全部请求。结合Blueprint的before_request,则是过滤该蓝图下的请求。所以我们就可以进行分层过滤,定制化过滤。

全局的场景包含:共享session的鉴权函数、请求黑白名单过滤、根据endpoint进行请求j等。

蓝图场景包含api的请求必填字段校验,是否json请求校验,请求的token校验等。

api = Blueprint('api', __name__)
requied = {
    'api.register':['email','username','password']
}

# 钩子 在请求执行之前
@api.before_request
def before_request():

    # 请求格式校验拦截
    if not request.is_json:
        return '带参数请求请使用json格式'
    # 缺少必填参数拦截
    try:
        if request.endpoint in requied:
            if request.method == "POST":
                missparam_list = [x for x in requied[request.endpoint] if x.encode('utf8'not in list(parse.parse_qs(request.data).keys())]
            else:
                missparam_list = [x for x in requied[request.endpoint] if x not in request.json.keys()]

            if len(missparam_list) > 0:
                return "缺少以下参数:{0}"
    except Exception as e:
        app.logger.error(e)
        return "{0}".format(e)

第三个钩子:@app.errorhandler

当访问应用出错时,根据错误响应码,进行一些定制化的操作,如返回一个可爱的404页面。也可以进行一些报错登记。

场景:可以用redis进行错误请求计数,超过一定量则进行告警。可以重定向到一个定制的错误代码页面等。

@app.errorhandler(404)
def page_not_found(error):
    return render_template('otherpage/404.html'),404

第四个钩子:@app.context_processor

这个钩子也很实用,是将一些常量按字典的格式返回,则可以在jinja2的模版中引用。这样就不用在每个视图函数中都render_template中重复去写一次。代码更简洁。

场景:在html中,直接用{{jidan}}就会在页面显示yao。等同于app.add_template_global('yao',''jidan)

@app.context_processor
def context_rocessor():
    return {'jidan':'yao'}

第五个钩子:@app.after_request

和上个钩子类似,差别在于是请求完成时执行,它和之前钩子有点不同,必须传入一个参数来接收响应对象,并在最后return 这个参数,也就是返回响应内容。

场景:一般用于格式化响应结果,包括响应请求头,响应的格式等。

@app.after_request
def after_request(response):
    response.headers['jidan'] = 'yaoyao'
    return response

第六个钩子:@app.teardown_request

和第五个钩子功能类似,在响应销毁时,执行一个绑定的函数。做一些操作。

区别点在于:

  • after_request: 每一个请求之后绑定一个函数,如果请求没有异常。

  • teardown_request: 每一个请求之后绑定一个函数,即使遇到了异常。

场景:销毁DB连接等。

@app.teardown_request
def teardown_db(exception):
    db = getattr(g, 'database'None)
    if db is not None:
        db.close()

第七个钩子:@app.teardown_appcontext

之前介绍的大部分是请求上下文的钩子,这个属于应用上下文的钩子。不管是否有异常,当APP上下文被移除之后执行的函数, 可以进行数据库的提交或者回滚。

场景:DB事务操作。

@app.teardown_appcontext
def teardown(cmd=None):
    if cmd is None:
        db.session.commit()
    else:
        db.session.rollback()
    db.session.remove()



分享到:

发表评论

评论列表