Skip to content

请求钩子

在客户端和服务器交互的过程中,有些准备工作或扫尾工作需要处理,比如:

  • 在请求开始时,建立数据库连接;
  • 在请求开始时,根据需求进行权限校验;
  • 在请求结束时,指定数据的交互格式;

为了让每个视图函数避免编写重复功能的代码,Flask 提供了通用设施的功能,即请求钩子。

请求钩子是通过装饰器的形式实现,Flask 支持如下的请求钩子:

钩 子说 明
before_request在每次请求前执行
after_request如果没有未处理的异常抛出,会在每个请求结束后运行
teardown_request在每次请求后执行,如果有相关错误抛出

这些钩子使用起来和 app.route() 装饰器基本相同,每个钩子可以注册任意多个处理函数,函数名并不是必须和钩子名称相同,下面是一个基本示例:

python
from flask import Flask
from flask import abort

app = Flask(__name__)


# 每一次请求被处理之前调用
# 如果请求的校验不成功,可以直接在此方法中进行响应,直接 return 之后那么就不会执行视图函数
@app.before_request
def before_request():
    # 每一个请求被视图函数处理之前被调用
    print("每次请求都会触发 before_request")
    # if 不符合条件:
    #     return "请求错误"


# 在执行完视图函数之后会调用,并且会把视图函数所生成的响应传入,可以在此方法中对响应做最后一步统一的处理
@app.after_request
def after_request(response):
    print("每次请求返回到浏览器之前触发 after_request")
    response.headers["Content-Type"] = "application/json"
    return response


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


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

假如我们创建了三个视图函数 A、B、C,其中视图 C 使用了 after_this_request 钩子,那么当请求 A 进入后,就会执行下面的请求逻辑。

请求处理函数调用示意图

下面是请求钩子的一些常见应用场景:

  • before_request

    比如⽹站上要记录用户最后在线的时间,可以通过用户最后发送的请求时间来实现。为了避免在每个视图函数都添加更新在线 时间的代码,我们可以仅在使用 before_request 钩子注册的函数中调用这段代码。

  • after_request

    我们经常在视图函数中进行数据库操作,比如更新、 插入等,之后需要将更改提交到数据库中。提交更改的代码就可以放到 after_request 钩子注册的函数中。

在 Flask 程序中,客户端发出的请求触发相应的视图函数,获取返回值会作为响应的主体,最后生成完整的响应,即响应报文。

flask 上下文

上下文:即语境,语意,在程序中可以理解为在代码执行到某一时刻时,根据之前代码所做的操作以及下文即将要执行的逻辑,可以决定在当前时刻下可以使用到的变量,或者可以完成的事情。

Flask 中有两种上下文,请求上下文和应用上下文

Flask 中上下文对象:相当于一个容器,保存了 Flask 程序运行过程中的一些信息。

在很多情况下,要执行某一操作,需要做很多的准备工作!而这一操作前必须满足的状态,就是当前操作的上文。由此及彼,当前操作之后要达到的状态就是当前操作的下文!其实就是中文里所谓的语境,就是语言环境。

变量名上下文类别说 明
current_app程序上下文指向处理请求的当前程序实例
g程序上下文替代Python的全局变量用法,确保仅在当前请求中可用。用于存储全局数据,每次请求都会重设
request请求上下文封装客户端发出的请求报文数据
session请求上下文用于记住请求之间的数据,通过签名的Cookie实现

请求上下文

请求上下文(request context) 对象有:requestsession

思考:在视图函数中,如何取到当前请求的相关数据?比如:请求地址,请求方式,cookie等等

在 flask 中,可以直接在视图函数中使用 request 这个对象进行获取相关数据,而 request 就是请求上下文的对象,保存了当前本次请求的相关数据

  • request
    • 封装了 HTTP 请求的内容,针对的是 http 请求。
    • 举例:user = request.args.get('user'),获取的是 get 请求的参数。
  • session
    • 用来记录请求会话中的信息,针对的是用户信息。
    • 举例:session['name'] = user.id,可以记录用户信息。
    • 还可以通过 session.get('name') 获取用户信息。

应用上下文

应用上下文(application context) 对象有:current_app,g

它的字面意思是 应用上下文,但它不是一直存在的,它只是 request context 中的一个对 app 的代理(人),所谓 local proxy。它的作用主要是帮助 request 获取当前的应用,它是伴 request 而生,随 request 而灭的。

current_app

应用程序上下文,用于存储应用程序中的变量,可以通过 current_app.name 打印当前 app 的名称,也可以在 current_app 中存储一些变量,例如:

  • 应用的启动脚本是哪个文件,启动时指定了哪些参数
  • 加载了哪些配置文件,导入了哪些配置
  • 连了哪个数据库
  • 有哪些 public 的工具类、常量
  • 应用跑在哪个机器上,IP 多少,内存多大

示例

python
# -*- coding: utf-8 -*-
from flask import Flask, current_app

app = Flask(__name__)

# 以 secret_key 对象为例
# 为了方便在各个视图中使用,将创建的 secret_key 变量保存到 flask app 中,
# 后续可以在视图中使用 current_app.secret_key 获取
app.secret_key = 'app1 秘钥'


@app.route('/')
def route_view():
    # current_app 是当前请求的对象,是app对象的一个代理对象,只能读取数据不能修改
    return current_app.secret_key

作用

current_app 就是当前运行的 flask app,在代码不方便直接操作 flask 的 app 对象时,可以操作 current_app 就等价于操作 flask app 对象

变量 g

g 作为 flask 程序全局的一个临时变量,充当中间媒介的作用,我们可以通过它在一次请求调用的多个函数间传递一些数据。每次请求都会重设这个变量。

大的区别是,session 对象是可以跨 request 的,只要 session 还未失效,不同的 request 的请求会获取到同一个session,但是 g 对象不是,g 对象不需要管过期时间,请求一次就 g 对象就改变了一次,或者重新赋值了一次。那么 g 对象该如何使用呢?

python
from flask import Flask, g, session

app = Flask(__name__)
app.secret_key = '123456'

students = [
    {"id": 1, "name": "正心", "gender": "男"},
    {"id": 2, "name": "山禾", "gender": "男"},
    {"id": 3, "name": "丸子", "gender": "女"},
]


def db_query():
    """查询用户数据"""
    for student in students:
        if student['id'] == g.user_id:  # 2. 直接使用  
            return student
    return {}


@app.route("/")
def get_user_profile():


# 1. 从 session 里面获取变量, 绑定到 g 对象上面   
g.user_id = session.get("user_id")
user = db_query()
return "hello world"

注意:不同的请求,会有不同的全局变量

两者区别:

  • 请求上下文:保存了客户端和服务器交互的数据
  • 应用上下文:flask 应用程序运行过程中,保存的一些配置信息,比如程序名、数据库连接、应用信息等