Flask中特殊装饰器的使用
作者:孤寒者
Flask中用作装饰器的特殊的函数 第一部分:before_request和after_request
在Flask中,before_request
和after_request
是用作装饰器的特殊函数,它们可以用来在请求处理过程中执行某些操作。
一、 before_request装饰器:
- 通过在函数上使用
@app.before_request
装饰器,可以将该函数注册为全局的请求前钩子(hook)。这意味着每次请求到达服务器时,在实际处理请求之前,都会先执行被before_request
装饰的函数。 before_request
函数通常用于执行一些预处理任务,例如验证用户身份、设置全局变量、打开数据库连接等。它可以修改请求或应用程序上下文,并且可以返回响应对象或 None(后面扒扒源码看看)。
比如:
@app.before_request def before_request(): # 执行一些预处理任务 if not current_user.is_authenticated: return redirect(url_for('login'))
二、after_request装饰器:
- 通过在函数上使用
@app.after_request
装饰器,可以将该函数注册为全局的请求后钩子。这意味着在每次请求完成并返回响应之后,都会执行被after_request
装饰的函数。 after_request
函数通常用于执行一些后处理任务,例如**添加响应头、记录请求日志、关闭数据库连接等。**它接收一个参数,即响应对象,并且必须返回一个响应对象。
比如:
@app.after_request def after_request(response): # 执行一些后处理任务 response.headers['X-Frame-Options'] = 'SAMEORIGIN' return response
通过使用before_request
和after_request
装饰器,可以在请求的前后执行一些共同的逻辑,从而实现全局的预处理和后处理操作。这样可以避免在每个视图函数中重复编写相同的代码。
三、多个before_request和after_request执行流程分析:
首先—理论讲解:
当存在多个before_request
和after_request
装饰器时,分析它们的执行顺序:
before_request
执行流程:
- 当一个请求到达服务器时,首先会执行第一个注册的
before_request
装饰的函数。 - 如果该函数返回了一个响应对象,则停止执行后续所有的
before_request
函数,而是直接返回该响应对象给客户端。 - 如果该函数没有返回响应对象,则继续执行下一个注册的
before_request
函数,以此类推,直到所有的before_request
函数都被执行完毕。
after_request
执行流程:
- 在每次请求完成并返回响应之后,从最后一个注册的
after_request
装饰的函数开始执行。 - 每个
after_request
函数都会接收前一个after_request
函数所返回的响应对象作为参数,并且必须返回一个新的响应对象。 - 执行完最后一个
after_request
函数后,最终的响应对象将会发送给客户端。
然后—实战讲解:
from flask import Flask, session app = Flask(__name__) # 创建Flask应用程序对象 @app.before_request def before_request_1(): print("Before Request 1") @app.before_request def before_request_2(): print("Before Request 2") @app.after_request def after_request_1(response): print("After Request 1") return response @app.after_request def after_request_2(response): print("After Request 2") return response @app.route('/') def index(): print("Index Page") return "Hello, World!" if __name__ == '__main__': app.run()
输出的执行顺序如下:
可以看到,首先执行了before_request_1
和before_request_2
两个函数,然后处理了请求并返回响应,接着按照相反的顺序执行了after_request_2
和after_request_1
两个函数。
最后—扒扒源码:
为何before_request
是按注册顺序执行,而after_request
是按注册倒序执行嘞?
扒一扒源码就晓得啦~
直接进入before_request
和after_request
的身体:
上面就是Flask注册before_request
和after_request
函数的方法,一模一样!
下面来扒下Flask执行before_request
和after_request
函数部分的源码:
进入app.__call__()
后直到full_dispatch_request
函数:
关注点先放在上图第二个for循环,其余部分后续会慢慢探究~
before_request_funcs
就是上述包含了请求前钩子函数的字典。这些函数会在请求分发(即dispatch_request
)之前被调用。
遍历每个注册的请求前钩子函数,并执行它们。如果任何一个请求前钩子函数**返回一个非 None 的值,该值会被当作视图函数的返回值处理(直接返回给客户端页面),并且后续的请求处理流程会被停止【但是after_request正常执行!】;**如果没有任何请求前钩子函数返回非 None 值,那么 preprocess_request
方法会返回 None。
来个代码讲解:
画图形象记忆:
绿色就是所有请求前钩子函数(before_request
)返回值都为None时的执行流程;
相应的,白色就是有一个返回值非None时的执行流程。
差点忘了带大家看after_request
了!
看到没!reversed!!!反向遍历哦~
第二部分:实战—使用before_request进行身份验证
上代码:
from flask import Flask, request, redirect, url_for, render_template, session app = Flask(__name__) # 模拟的用户数据库 users = { 'admin': { 'username': 'admin', 'password': 'GuHanZhe' } } @app.before_request def authenticate_user(): # 获取当前请求的路径 path = request.path # 如果请求的路径不是登录页面,进行身份验证 if path != '/login': # 检查 session 中是否存在已登录的用户 if 'username' not in session: # 用户未登录,重定向到登录页面 return redirect(url_for('login')) @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] # 在实际开发中,这里需要进行密码验证 if username in users and users[username]['password'] == password: # 登录成功,将用户名保存在 session 中 session['username'] = username return redirect(url_for('protected_page')) else: # 登录失败,显示错误信息 error_message = "Invalid username or password." return render_template('login.html', error_message=error_message) return render_template('login.html') @app.route('/protected') def protected_page(): return "This is a protected page. Only logged-in users can access it." if __name__ == '__main__': app.run()
在上述代码中,before_request
钩子函数 authenticate_user
用于验证用户身份。它会在每个请求到达之前被调用,除了登录页面 /login
外的所有页面都需要进行身份验证。
如果用户未登录,authenticate_user
函数将重定向到登录页面,使用 redirect
函数和 url_for
函数实现页面重定向。登录成功后,将用户名保存在 session
中,以便在后续的请求中进行验证。
login
路由处理函数负责渲染登录页面,并接收用户提交的表单数据。在实际应用中,需要根据数据库中的用户信息进行密码验证。验证成功后,将用户名保存在 session
中,并重定向到受保护的页面 /protected
。
protected_page
路由处理函数是一个示例的受保护页面,只有登录用户可见。
第三部分:补充常见特殊装饰器
一、@app.errorhandler(code):
- 这是用于注册错误处理函数的装饰器。
code
参数指定了需要处理的错误码,例如 404、500 等。- 装饰的函数将作为错误处理函数,在出现指定错误码时被调用,并返回自定义的错误页面或响应。
二、 @app.teardown_request:
用于注册在每个请求结束时执行的函数。它可以用来进行一些清理操作或释放资源。
实战:
from flask import Flask app = Flask(__name__) @app.route('/') def index(): return "Hello, World!" @app.teardown_request def teardown_request_func(error=None): print("Teardown function is called after each request.") if __name__ == '__main__': app.run()
在上述代码中,定义了一个名为 teardown_request_func
的函数,并使用 @app.teardown_request
装饰器将其注册为每个请求结束时执行的函数。
当我们访问任何路由时,Flask 会在请求结束后自动调用 teardown_request_func
函数。无论请求是否出现错误,该函数都会被执行。
需要注意的是,@app.teardown_request
装饰的函数只能接受一个参数,即可选的错误对象。如果要访问请求上下文中的其他对象,可以使用 flask.request
对象。
这个装饰器通常用于进行一些清理操作,例如关闭数据库连接、释放资源等。
三、@app.template_test:
用于注册自定义模板测试函数的装饰器。
实战:
from flask import Flask app = Flask(__name__) @app.template_test('even') def is_even(number): return number % 2 == 0 if __name__ == '__main__': app.run()
通过 @app.template_test('even')
装饰器将 is_even
函数注册为一个名为 “even” 的模板测试函数。该函数用于判断一个数字是否是偶数。
在模板中,可以使用 {% if %}
语句来调用这个模板测试函数:
{% if num is even %} <p>The number is even</p> {% else %} <p>The number is odd</p> {% endif %}
通过注册模板测试函数,我们可以在模板中使用自定义的逻辑判断函数,
以便根据特定的条件进行动态渲染和显示不同的内容。
四、@app.before_first_request:
用于注册在第一个请求到达之前执行的函数。它只会在应用程序启动时执行一次。
实战:
from flask import Flask app = Flask(__name__) @app.before_first_request def before_first_request_func(): print("This function is executed before the first request.") @app.route('/') def index(): return "Hello, World!" if __name__ == '__main__': app.run()
在上述代码中,before_first_request_func
被装饰为 @app.before_first_request
,它会在第一个请求到达之前执行。
当我们运行这个应用程序时,before_first_request_func
函数会在第一个请求到达之前执行一次。之后,每个请求到达时,都不会再次调用该函数。
需要注意的是,@app.before_first_request
装饰的函数仅在主线程中执行,并且只有在应用程序启动时才会被调用一次。如果使用多线程或多进程部署应用程序,可以考虑使用其他方法来进行初始化操作。
【新版本Flask没有这个装饰器了~】
到此这篇关于Flask中特殊装饰器的使用的文章就介绍到这了,更多相关Flask 特殊装饰器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!